From 5eadd1d945da0493d7b2e304c19649274cd90d03 Mon Sep 17 00:00:00 2001 From: Eusebiu Marcu Date: Sat, 12 Feb 2011 09:35:26 +0200 Subject: [PATCH] Add SharpDevelop Dom --- .../AvalonEdit/Editor/ITextBuffer.cs | 10 + Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj | 5 + .../Services/ParserService/IParser.cs | 20 +- .../Services/ParserService/Parser.cs | 103 ++ ILSpy.sln | 28 +- .../Project/Configuration/AssemblyInfo.cs | 18 + .../ICSharpCode.SharpDevelop.Dom.csproj | 216 +++ .../Project/Src/Ambience.cs | 242 +++ .../Project/Src/BusyManager.cs | 52 + .../Project/Src/CSharp/CSharpAmbience.cs | 606 +++++++ .../Src/CSharp/CSharpExpressionContext.cs | 55 + .../Project/Src/CSharp/ExpressionFinder.cs | 1057 ++++++++++++ .../Project/Src/CSharp/OverloadResolution.cs | 340 ++++ .../Project/Src/CSharp/TypeInference.cs | 476 ++++++ .../Project/Src/CecilReader.cs | 564 +++++++ .../Project/Src/ClassFinder.cs | 130 ++ .../Project/Src/CtrlSpaceResolveHelper.cs | 285 ++++ .../Project/Src/DiffUtility.cs | 129 ++ .../Project/Src/DomCache.cs | 41 + .../Project/Src/EasyCodeDom.cs | 382 +++++ .../Project/Src/ExpressionContext.cs | 486 ++++++ .../Project/Src/ExtensionMethods.cs | 104 ++ .../Project/Src/FilePosition.cs | 114 ++ .../Project/Src/FileUtility.Minimal.cs | 112 ++ .../Project/Src/FoldingRegion.cs | 31 + .../Project/Src/FusionNative.cs | 275 +++ .../Project/Src/GacInterop.cs | 139 ++ .../Project/Src/HostCallback.cs | 62 + .../Project/Src/IComment.cs | 24 + .../Project/Src/IExpressionFinder.cs | 66 + .../Project/Src/IResolver.cs | 22 + .../Src/Implementations/AbstractEntity.cs | 358 ++++ .../Src/Implementations/AbstractMember.cs | 97 ++ .../Src/Implementations/AbstractReturnType.cs | 136 ++ .../AnonymousMethodReturnType.cs | 190 +++ .../Src/Implementations/ArrayReturnType.cs | 154 ++ .../Implementations/AttributeReturnType.cs | 47 + .../Src/Implementations/BoundTypeParameter.cs | 91 + .../Src/Implementations/CombinedReturnType.cs | 152 ++ .../Src/Implementations/CompoundClass.cs | 131 ++ .../Implementations/ConstructedReturnType.cs | 259 +++ .../Implementations/DecoratingReturnType.cs | 29 + .../Src/Implementations/DefaultAttribute.cs | 88 + .../Src/Implementations/DefaultClass.cs | 662 ++++++++ .../Src/Implementations/DefaultComment.cs | 47 + .../Implementations/DefaultCompilationUnit.cs | 160 ++ .../Src/Implementations/DefaultEvent.cs | 117 ++ .../Src/Implementations/DefaultField.cs | 94 ++ .../Src/Implementations/DefaultMethod.cs | 228 +++ .../Src/Implementations/DefaultParameter.cs | 173 ++ .../Src/Implementations/DefaultProperty.cs | 175 ++ .../Src/Implementations/DefaultReturnType.cs | 254 +++ .../Implementations/DefaultTypeParameter.cs | 194 +++ .../Src/Implementations/DefaultUsing.cs | 174 ++ .../Src/Implementations/DefaultUsingScope.cs | 66 + .../Src/Implementations/DynamicReturnType.cs | 51 + .../Src/Implementations/ElementReturnType.cs | 64 + .../Src/Implementations/GenericReturnType.cs | 125 ++ .../Src/Implementations/GetClassReturnType.cs | 104 ++ .../Implementations/MethodGroupReturnType.cs | 43 + .../Src/Implementations/NullReturnType.cs | 38 + .../Src/Implementations/PointerReturnType.cs | 105 ++ .../Src/Implementations/ProxyReturnType.cs | 212 +++ .../Implementations/ReferenceReturnType.cs | 56 + .../Implementations/SearchClassReturnType.cs | 126 ++ .../Src/Implementations/SystemTypes.cs | 135 ++ .../Src/Implementations/UnknownReturnType.cs | 18 + .../Project/Src/Interfaces/ClassType.cs | 14 + .../ExplicitInterfaceImplementation.cs | 51 + .../Project/Src/Interfaces/IAttribute.cs | 55 + .../Project/Src/Interfaces/IClass.cs | 150 ++ .../Src/Interfaces/ICompilationUnit.cs | 74 + .../Src/Interfaces/ICompletionEntry.cs | 18 + .../Src/Interfaces/IDomProgressMonitor.cs | 23 + .../Project/Src/Interfaces/IEntity.cs | 171 ++ .../Project/Src/Interfaces/IEvent.cs | 22 + .../Project/Src/Interfaces/IField.cs | 16 + .../Project/Src/Interfaces/IFreezable.cs | 103 ++ .../Project/Src/Interfaces/IMember.cs | 54 + .../Project/Src/Interfaces/IMethod.cs | 38 + .../Project/Src/Interfaces/IParameter.cs | 53 + .../Project/Src/Interfaces/IProperty.cs | 38 + .../Project/Src/Interfaces/IReturnType.cs | 145 ++ .../Project/Src/Interfaces/ITypeParameter.cs | 71 + .../Project/Src/Interfaces/IUsing.cs | 41 + .../Project/Src/Interfaces/IUsingScope.cs | 42 + .../Project/Src/Interfaces/ModifierEnum.cs | 46 + .../Src/Interfaces/ParameterModifiers.cs | 20 + .../Project/Src/Interfaces/Region.cs | 129 ++ .../Project/Src/LanguageProperties.cs | 556 +++++++ .../Project/Src/LazyList.cs | 95 ++ .../Project/Src/LoggingService.cs | 52 + .../Project/Src/MemberLookupHelper.cs | 871 ++++++++++ .../CSharpToVBNetConvertVisitor.cs | 546 ++++++ .../CodeSnippetConverter.cs | 202 +++ .../NRefactoryResolver/InferredReturnType.cs | 45 + .../LambdaParameterReturnType.cs | 65 + .../NRefactoryResolver/LambdaReturnType.cs | 106 ++ .../NRefactoryASTConvertVisitor.cs | 856 ++++++++++ .../NRefactoryInformationProvider.cs | 38 + .../NRefactoryResolver/NRefactoryResolver.cs | 1469 +++++++++++++++++ .../Src/NRefactoryResolver/ResolveVisitor.cs | 751 +++++++++ .../Src/NRefactoryResolver/TypeVisitor.cs | 130 ++ .../VBNetToCSharpConvertVisitor.cs | 703 ++++++++ .../Project/Src/ParameterListComparer.cs | 56 + .../ProjectContent/DefaultProjectContent.cs | 1082 ++++++++++++ .../Src/ProjectContent/DomAssemblyName.cs | 93 ++ .../Src/ProjectContent/IProjectContent.cs | 287 ++++ .../Src/ProjectContent/ParseInformation.cs | 62 + .../ProjectContent/ProjectContentRegistry.cs | 338 ++++ .../ReflectionProjectContent.cs | 201 +++ .../Project/Src/ReadOnlyDictionary.cs | 99 ++ .../Src/Refactoring/CSharpCodeGenerator.cs | 110 ++ .../Project/Src/Refactoring/CodeGenerator.cs | 868 ++++++++++ .../Src/Refactoring/CodeGeneratorOptions.cs | 24 + .../Src/Refactoring/IRefactoringDocument.cs | 125 ++ .../Refactoring/NRefactoryCodeGenerator.cs | 34 + .../NRefactoryRefactoringProvider.cs | 635 +++++++ .../Src/Refactoring/RefactoringProvider.cs | 93 ++ .../Project/Src/Refactoring/TextFinder.cs | 41 + .../Src/Refactoring/VBNetCodeGenerator.cs | 43 + .../Src/ReflectionLayer/DomPersistence.cs | 1060 ++++++++++++ .../Src/ReflectionLayer/ReflectionClass.cs | 232 +++ .../Src/ReflectionLayer/ReflectionEvent.cs | 62 + .../Src/ReflectionLayer/ReflectionField.cs | 44 + .../Src/ReflectionLayer/ReflectionMethod.cs | 80 + .../ReflectionLayer/ReflectionParameter.cs | 36 + .../Src/ReflectionLayer/ReflectionProperty.cs | 107 ++ .../ReflectionLayer/ReflectionReturnType.cs | 209 +++ .../ReflectionTypeNameSyntaxError.cs | 31 + .../Project/Src/ResolveResult.cs | 1060 ++++++++++++ .../Project/Src/SignatureComparer.cs | 39 + .../Project/Src/Tag.cs | 46 + .../Project/Src/VBNet/IVBNetOptionProvider.cs | 24 + .../Project/Src/VBNet/VBNetAmbience.cs | 627 +++++++ .../Project/Src/VBNet/VBNetCompilationUnit.cs | 51 + .../Src/VBNet/VBNetExpressionFinder.cs | 269 +++ .../Project/Src/XmlDoc.cs | 356 ++++ Libraries/log4net/LICENSE.txt | 201 +++ Libraries/log4net/log4net.dll | Bin 0 -> 270336 bytes 140 files changed, 27920 insertions(+), 11 deletions(-) create mode 100644 Debugger/ILSpy.Debugger/Services/ParserService/Parser.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Configuration/AssemblyInfo.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Ambience.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/BusyManager.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/CSharpAmbience.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/CSharpExpressionContext.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/ExpressionFinder.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/OverloadResolution.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/TypeInference.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ClassFinder.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CtrlSpaceResolveHelper.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/DiffUtility.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/DomCache.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/EasyCodeDom.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ExpressionContext.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ExtensionMethods.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FilePosition.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FileUtility.Minimal.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FoldingRegion.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FusionNative.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/GacInterop.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/HostCallback.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/IComment.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/IExpressionFinder.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/IResolver.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractEntity.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractMember.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AnonymousMethodReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ArrayReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AttributeReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/BoundTypeParameter.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CombinedReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CompoundClass.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ConstructedReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DecoratingReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultAttribute.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultComment.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultCompilationUnit.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultEvent.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultField.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultMethod.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultParameter.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultProperty.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultTypeParameter.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultUsing.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultUsingScope.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DynamicReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ElementReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/GenericReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/GetClassReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/MethodGroupReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/NullReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/PointerReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ProxyReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ReferenceReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SearchClassReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SystemTypes.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/UnknownReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ClassType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ExplicitInterfaceImplementation.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IAttribute.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IClass.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ICompilationUnit.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ICompletionEntry.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IDomProgressMonitor.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IEntity.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IEvent.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IField.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IFreezable.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IMember.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IMethod.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IParameter.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IProperty.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ITypeParameter.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IUsing.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IUsingScope.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ModifierEnum.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ParameterModifiers.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/Region.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/LazyList.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/LoggingService.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CSharpToVBNetConvertVisitor.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CodeSnippetConverter.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/InferredReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/LambdaParameterReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/LambdaReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryASTConvertVisitor.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryInformationProvider.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/TypeVisitor.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/VBNetToCSharpConvertVisitor.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ParameterListComparer.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DomAssemblyName.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/IProjectContent.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ParseInformation.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ProjectContentRegistry.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ReflectionProjectContent.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReadOnlyDictionary.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CSharpCodeGenerator.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CodeGenerator.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CodeGeneratorOptions.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/IRefactoringDocument.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/NRefactoryCodeGenerator.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/NRefactoryRefactoringProvider.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/RefactoringProvider.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/TextFinder.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/VBNetCodeGenerator.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/DomPersistence.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionEvent.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionField.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionMethod.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionParameter.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionProperty.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionReturnType.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionTypeNameSyntaxError.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ResolveResult.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/SignatureComparer.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Tag.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/IVBNetOptionProvider.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/VBNetAmbience.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/VBNetCompilationUnit.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/VBNetExpressionFinder.cs create mode 100644 Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/XmlDoc.cs create mode 100644 Libraries/log4net/LICENSE.txt create mode 100644 Libraries/log4net/log4net.dll diff --git a/Debugger/ILSpy.Debugger/AvalonEdit/Editor/ITextBuffer.cs b/Debugger/ILSpy.Debugger/AvalonEdit/Editor/ITextBuffer.cs index 9ca640874..269837577 100644 --- a/Debugger/ILSpy.Debugger/AvalonEdit/Editor/ITextBuffer.cs +++ b/Debugger/ILSpy.Debugger/AvalonEdit/Editor/ITextBuffer.cs @@ -20,6 +20,8 @@ using System; using System.Collections.Generic; using System.IO; +using ICSharpCode.AvalonEdit.Document; + namespace ILSpy.Debugger.AvalonEdit.Editor { /// @@ -133,4 +135,12 @@ namespace ILSpy.Debugger.AvalonEdit.Editor /// Raised if 'other' belongs to a different document than this checkpoint. int MoveOffsetTo(ITextBufferVersion other, int oldOffset, AnchorMovementType movement); } + + public sealed class StringTextBuffer : AvalonEditTextSourceAdapter + { + public StringTextBuffer(string text) + : base(new StringTextSource(text)) + { + } + } } diff --git a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj index b06302c9a..3e7be1736 100644 --- a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj +++ b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj @@ -71,6 +71,7 @@ + AttachToProcessWindow.xaml @@ -102,6 +103,10 @@ {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195} NRefactory + + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} + ICSharpCode.SharpDevelop.Dom + {1D18D788-F7EE-4585-A23B-34DC8EC63CB8} Debugger.Core diff --git a/Debugger/ILSpy.Debugger/Services/ParserService/IParser.cs b/Debugger/ILSpy.Debugger/Services/ParserService/IParser.cs index 55370b5e0..24a703d81 100644 --- a/Debugger/ILSpy.Debugger/Services/ParserService/IParser.cs +++ b/Debugger/ILSpy.Debugger/Services/ParserService/IParser.cs @@ -20,13 +20,21 @@ using System; namespace ILSpy.Debugger.Services { - /// - /// Description of IParser. - /// - public class IParser + public interface IParser { - public IParser() - { + string[] LexerTags { + get; + set; } + + LanguageProperties Language { + get; + } + + IExpressionFinder CreateExpressionFinder(string fileName); + + ICompilationUnit Parse(IProjectContent projectContent, string fileName = null, ITextBuffer fileContent); + + IResolver CreateResolver(); } } diff --git a/Debugger/ILSpy.Debugger/Services/ParserService/Parser.cs b/Debugger/ILSpy.Debugger/Services/ParserService/Parser.cs new file mode 100644 index 000000000..4856f3900 --- /dev/null +++ b/Debugger/ILSpy.Debugger/Services/ParserService/Parser.cs @@ -0,0 +1,103 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +using System; +using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Dom.CSharp; +using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver; + +namespace ILSpy.Debugger.Services.ParserService +{ + public class TParser : IParser + { + ///IParser Interface + string[] lexerTags; + + public string[] LexerTags { + get { + return lexerTags; + } + set { + lexerTags = value; + } + } + + public LanguageProperties Language { + get { + return LanguageProperties.CSharp; + } + } + + public IExpressionFinder CreateExpressionFinder(string fileName) + { + return new CSharpExpressionFinder(null); + } + + void RetrieveRegions(ICompilationUnit cu, ICSharpCode.NRefactory.Parser.SpecialTracker tracker) + { + for (int i = 0; i < tracker.CurrentSpecials.Count; ++i) { + ICSharpCode.NRefactory.PreprocessingDirective directive = tracker.CurrentSpecials[i] as ICSharpCode.NRefactory.PreprocessingDirective; + if (directive != null) { + if (directive.Cmd == "#region") { + int deep = 1; + for (int j = i + 1; j < tracker.CurrentSpecials.Count; ++j) { + ICSharpCode.NRefactory.PreprocessingDirective nextDirective = tracker.CurrentSpecials[j] as ICSharpCode.NRefactory.PreprocessingDirective; + if (nextDirective != null) { + switch (nextDirective.Cmd) { + case "#region": + ++deep; + break; + case "#endregion": + --deep; + if (deep == 0) { + cu.FoldingRegions.Add(new FoldingRegion(directive.Arg.Trim(), DomRegion.FromLocation(directive.StartPosition, nextDirective.EndPosition))); + goto end; + } + break; + } + } + } + end: ; + } + } + } + } + + public ICompilationUnit Parse(IProjectContent projectContent, string fileName = null, ITextBuffer fileContent) + { + using (ICSharpCode.NRefactory.IParser p = ICSharpCode.NRefactory.ParserFactory.CreateParser(ICSharpCode.NRefactory.SupportedLanguage.CSharp, fileContent.CreateReader())) { + return Parse(p, fileName, projectContent); + } + } + + ICompilationUnit Parse(ICSharpCode.NRefactory.IParser p, string fileName, IProjectContent projectContent) + { + p.Lexer.SpecialCommentTags = lexerTags; + p.ParseMethodBodies = false; + p.Parse(); + + NRefactoryASTConvertVisitor visitor = new NRefactoryASTConvertVisitor(projectContent, ICSharpCode.NRefactory.SupportedLanguage.CSharp); + visitor.Specials = p.Lexer.SpecialTracker.CurrentSpecials; + visitor.VisitCompilationUnit(p.CompilationUnit, null); + visitor.Cu.FileName = fileName; + visitor.Cu.ErrorsDuringCompile = p.Errors.Count > 0; + RetrieveRegions(visitor.Cu, p.Lexer.SpecialTracker); + AddCommentTags(visitor.Cu, p.Lexer.TagComments); + return visitor.Cu; + } + + void AddCommentTags(ICompilationUnit cu, System.Collections.Generic.List tagComments) + { + foreach (ICSharpCode.NRefactory.Parser.TagComment tagComment in tagComments) { + DomRegion tagRegion = new DomRegion(tagComment.StartPosition.Y, tagComment.StartPosition.X); + var tag = new ICSharpCode.SharpDevelop.Dom.TagComment(tagComment.Tag, tagRegion, tagComment.CommentText); + cu.TagComments.Add(tag); + } + } + + public IResolver CreateResolver() + { + return new NRefactoryResolver(LanguageProperties.CSharp); + } + ///////// IParser Interface END + } +} diff --git a/ILSpy.sln b/ILSpy.sln index aa9048dc0..dfdc4dd20 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -6,19 +6,25 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Debugger", "Debugger", "{BF ProjectSection(SolutionItems) = postProject EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.Core", "Debugger\Debugger.Core\Debugger.Core.csproj", "{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.Debugger", "Debugger\ILSpy.Debugger\ILSpy.Debugger.csproj", "{6D3D0F0D-348D-456A-A6ED-E9BD5EFABB6A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.Core", "Debugger\Debugger.Core\Debugger.Core.csproj", "{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy", "ILSpy\ILSpy.csproj", "{1E85EFF9-E370-4683-83E4-8A3D063FF791}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TreeView", "SharpTreeView\ICSharpCode.TreeView.csproj", "{DDE2A481-8271-4EAC-A330-8FA6A38D13D1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "Mono.Cecil\Mono.Cecil.csproj", "{D68133BD-1E63-496E-9EDE-4FBDBF77B486}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler", "ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj", "{984CC812-9470-4A13-AFF9-CC44068D666C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{4E076A9B-159A-45C4-9E34-AE1D20D83E42}" + ProjectSection(SolutionItems) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Dom", "Libraries\ICSharpCode.SharpDevelop.Dom\Project\ICSharpCode.SharpDevelop.Dom.csproj", "{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.AvalonEdit", "AvalonEdit\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj", "{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler", "ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj", "{984CC812-9470-4A13-AFF9-CC44068D666C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "Mono.Cecil\Mono.Cecil.csproj", "{D68133BD-1E63-496E-9EDE-4FBDBF77B486}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRefactory", "Decompiler\lib\NRefactory\Project\NRefactory.csproj", "{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}" EndProject @@ -94,9 +100,21 @@ Global {6D3D0F0D-348D-456A-A6ED-E9BD5EFABB6A}.Release|x86.ActiveCfg = Release|x86 {6D3D0F0D-348D-456A-A6ED-E9BD5EFABB6A}.Release|Any CPU.Build.0 = Release|x86 {6D3D0F0D-348D-456A-A6ED-E9BD5EFABB6A}.Release|Any CPU.ActiveCfg = Release|x86 + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3}.Debug|x86.Build.0 = Debug|Any CPU + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3}.Debug|x86.ActiveCfg = Debug|Any CPU + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3}.Release|x86.Build.0 = Release|Any CPU + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3}.Release|x86.ActiveCfg = Release|Any CPU + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3}.Release|Any CPU.Build.0 = Release|Any CPU + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution - {6D3D0F0D-348D-456A-A6ED-E9BD5EFABB6A} = {BF79A230-0918-47DF-8A36-776779A331DE} {1D18D788-F7EE-4585-A23B-34DC8EC63CB8} = {BF79A230-0918-47DF-8A36-776779A331DE} + {6D3D0F0D-348D-456A-A6ED-E9BD5EFABB6A} = {BF79A230-0918-47DF-8A36-776779A331DE} + {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} + {D68133BD-1E63-496E-9EDE-4FBDBF77B486} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} + {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} EndGlobalSection EndGlobal diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Configuration/AssemblyInfo.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Configuration/AssemblyInfo.cs new file mode 100644 index 000000000..96709c90a --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Configuration/AssemblyInfo.cs @@ -0,0 +1,18 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Security.Permissions; + +[assembly: CLSCompliant(true)] +[assembly: StringFreezing()] + +[assembly: Dependency("System.Core", LoadHint.Always)] + +[assembly: AssemblyTitle("ICSharpCode.SharpDevelop.Dom")] +[assembly: AssemblyDescription("Code-completion library")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj new file mode 100644 index 000000000..760173cbe --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj @@ -0,0 +1,216 @@ + + + + Library + ICSharpCode.SharpDevelop.Dom + ICSharpCode.SharpDevelop.Dom + Debug + AnyCPU + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} + False + ..\..\ICSharpCode.SharpDevelop.snk + False + File + bin\Debug\ + False + False + False + Auto + 132644864 + AnyCPU + 4096 + 4 + false + v4.0 + False + -Microsoft.Design#CA1002;-Microsoft.Design#CA1063;-Microsoft.Performance#CA1800;-Microsoft.Security#CA2104 + + + OnBuildSuccess + C:\Users\Eusebiu\AppData\Roaming\ICSharpCode/SharpDevelop4.1\Settings.SourceAnalysis + + + obj\Debug\ + False + DEBUG;TRACE + true + Full + True + Project + + + obj\Release\ + True + TRACE + False + None + False + + + + ..\..\..\Libraries\log4net\log4net.dll + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195} + NRefactory + + + {D68133BD-1E63-496E-9EDE-4FBDBF77B486} + Mono.Cecil + + + + \ No newline at end of file diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Ambience.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Ambience.cs new file mode 100644 index 000000000..3065d4229 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Ambience.cs @@ -0,0 +1,242 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + [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 fully qualified name for the member + /// + UseFullyQualifiedMemberNames = 0x10, + /// + /// Show modifiers (virtual, override, etc.) + /// + ShowModifiers = 0x20, + /// + /// Show the inheritance declaration + /// + ShowInheritanceList = 0x40, + + IncludeHtmlMarkup = 0x80, + /// + /// Show the return type + /// + ShowReturnType = 0x100, + /// + /// Use fully qualified names for return type and parameters. + /// + UseFullyQualifiedTypeNames = 0x200, + /// + /// Include opening brace (or equivalent) for methods or classes; + /// or semicolon (or equivalent) for field, events. + /// For properties, a block indicating if there is a getter/setter is included. + /// + IncludeBody = 0x400, + /// + /// Show the list of type parameters on method and class declarations. + /// Type arguments for parameter/return types are always shown. + /// + ShowTypeParameterList = 0x800, + + StandardConversionFlags = ShowParameterNames | + ShowAccessibility | + ShowParameterList | + ShowReturnType | + ShowModifiers | + ShowTypeParameterList | + ShowDefinitionKeyWord, + + All = 0xfff, + } + + public interface IAmbience + { + ConversionFlags ConversionFlags { + get; + set; + } + + string Convert(IClass c); + string ConvertEnd(IClass c); + + string Convert(IEntity e); + + string Convert(IField field); + string Convert(IProperty property); + string Convert(IEvent e); + + string Convert(IMethod m); + string ConvertEnd(IMethod m); + + string Convert(IParameter param); + string Convert(IReturnType returnType); + + string ConvertAccessibility(ModifierEnum accessibility); + + string WrapAttribute(string attribute); + string WrapComment(string comment); + + string GetIntrinsicTypeName(string dotNetTypeName); + } + + public abstract class AbstractAmbience : IAmbience + { + #if DEBUG + int ownerThread = System.Threading.Thread.CurrentThread.ManagedThreadId; + #endif + + [System.Diagnostics.Conditional("DEBUG")] + protected void CheckThread() + { + #if DEBUG + if (ownerThread != System.Threading.Thread.CurrentThread.ManagedThreadId) + throw new InvalidOperationException("Ambience may only be used by the thread that created it"); + #endif + } + + ConversionFlags conversionFlags = ConversionFlags.StandardConversionFlags; + + public ConversionFlags ConversionFlags { + get { + return conversionFlags; + } + set { + CheckThread(); + conversionFlags = value; + } + } + + public bool ShowReturnType { + get { + return (conversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType; + } + } + + public bool ShowAccessibility { + get { + return (conversionFlags & ConversionFlags.ShowAccessibility) == ConversionFlags.ShowAccessibility; + } + } + + public bool ShowParameterNames { + get { + return (conversionFlags & ConversionFlags.ShowParameterNames) == ConversionFlags.ShowParameterNames; + } + } + + public bool UseFullyQualifiedTypeNames { + get { + return (conversionFlags & ConversionFlags.UseFullyQualifiedTypeNames) == ConversionFlags.UseFullyQualifiedTypeNames; + } + } + + public bool ShowDefinitionKeyWord { + get { + return (conversionFlags & ConversionFlags.ShowDefinitionKeyWord) == ConversionFlags.ShowDefinitionKeyWord; + } + } + + public bool ShowParameterList { + get { + return (conversionFlags & ConversionFlags.ShowParameterList) == ConversionFlags.ShowParameterList; + } + } + + public bool ShowModifiers { + get { + return (conversionFlags & ConversionFlags.ShowModifiers) == ConversionFlags.ShowModifiers; + } + } + + public bool ShowInheritanceList { + get { + return (conversionFlags & ConversionFlags.ShowInheritanceList) == ConversionFlags.ShowInheritanceList; + } + } + + public bool IncludeHtmlMarkup { + get { + return (conversionFlags & ConversionFlags.IncludeHtmlMarkup) == ConversionFlags.IncludeHtmlMarkup; + } + } + + public bool UseFullyQualifiedMemberNames { + get { + return (conversionFlags & ConversionFlags.UseFullyQualifiedMemberNames) == ConversionFlags.UseFullyQualifiedMemberNames; + } + } + + public bool IncludeBody { + get { + return (conversionFlags & ConversionFlags.IncludeBody) == ConversionFlags.IncludeBody; + } + } + + public bool ShowTypeParameterList { + get { + return (conversionFlags & ConversionFlags.ShowTypeParameterList) == ConversionFlags.ShowTypeParameterList; + } + } + + public abstract string Convert(IClass c); + public abstract string ConvertEnd(IClass c); + public abstract string Convert(IField c); + public abstract string Convert(IProperty property); + public abstract string Convert(IEvent e); + public abstract string Convert(IMethod m); + public abstract string ConvertEnd(IMethod m); + public abstract string Convert(IParameter param); + public abstract string Convert(IReturnType returnType); + + public virtual string Convert(IEntity entity) + { + if (entity == null) + throw new ArgumentNullException("entity"); + IClass c = entity as IClass; + if (c != null) + return Convert(c); + IMethod m = entity as IMethod; + if (m != null) + return Convert(m); + IField f = entity as IField; + if (f != null) + return Convert(f); + IProperty p = entity as IProperty; + if (p != null) + return Convert(p); + IEvent e = entity as IEvent; + if (e != null) + return Convert(e); + return entity.ToString(); + } + + public abstract string WrapAttribute(string attribute); + public abstract string WrapComment(string comment); + public abstract string GetIntrinsicTypeName(string dotNetTypeName); + public abstract string ConvertAccessibility(ModifierEnum accessibility); + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/BusyManager.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/BusyManager.cs new file mode 100644 index 000000000..e7b8f5b0e --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/BusyManager.cs @@ -0,0 +1,52 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// 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. + /// + sealed class BusyManager + { + public struct BusyLock : IDisposable + { + public static readonly BusyLock Failed = new BusyLock(null); + + readonly BusyManager manager; + + public BusyLock(BusyManager manager) + { + this.manager = manager; + } + + public bool Success { + get { return manager != null; } + } + + public void Dispose() + { + if (manager != null) { + manager.activeObjects.RemoveAt(manager.activeObjects.Count - 1); + } + } + } + + readonly List activeObjects = new List(); + + public BusyLock Enter(object obj) + { + for (int i = 0; i < activeObjects.Count; i++) { + if (activeObjects[i] == obj) + return BusyLock.Failed; + } + activeObjects.Add(obj); + return new BusyLock(this); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/CSharpAmbience.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/CSharpAmbience.cs new file mode 100644 index 000000000..b702338a1 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/CSharpAmbience.cs @@ -0,0 +1,606 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.Linq; + +namespace ICSharpCode.SharpDevelop.Dom.CSharp +{ + public class CSharpAmbience : AbstractAmbience + { + public static IDictionary TypeConversionTable { + get { return ICSharpCode.NRefactory.Ast.TypeReference.PrimitiveTypesCSharpReverse; } + } + + bool ModifierIsSet(ModifierEnum modifier, ModifierEnum query) + { + return (modifier & query) == query; + } + + public override string ConvertAccessibility(ModifierEnum accessibility) + { + if (ShowAccessibility) { + if (ModifierIsSet(accessibility, ModifierEnum.Public)) { + return "public "; + } else if (ModifierIsSet(accessibility, ModifierEnum.Private)) { + return "private "; + } else if (ModifierIsSet(accessibility, ModifierEnum.ProtectedAndInternal)) { + return "protected internal "; + } else if (ModifierIsSet(accessibility, ModifierEnum.Internal)) { + return "internal "; + } else if (ModifierIsSet(accessibility, ModifierEnum.Protected)) { + return "protected "; + } + } + + return string.Empty; + } + + string GetModifier(IEntity decoration) + { + string ret = ""; + + if (IncludeHtmlMarkup) { + ret += ""; + } + + if (decoration.IsStatic) { + ret += "static "; + } else if (decoration.IsSealed) { + ret += "sealed "; + } else if (decoration.IsVirtual) { + ret += "virtual "; + } else if (decoration.IsOverride) { + ret += "override "; + } else if (decoration.IsNew) { + ret += "new "; + } + + if (IncludeHtmlMarkup) { + ret += ""; + } + + return ret; + } + + public override string Convert(IClass c) + { + CheckThread(); + + StringBuilder builder = new StringBuilder(); + + builder.Append(ConvertAccessibility(c.Modifiers)); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (ShowModifiers) { + if (c.IsStatic) { + builder.Append("static "); + } else if (c.IsSealed) { + switch (c.ClassType) { + case ClassType.Delegate: + case ClassType.Struct: + case ClassType.Enum: + break; + + default: + builder.Append("sealed "); + break; + } + } else if (c.IsAbstract && c.ClassType != ClassType.Interface) { + builder.Append("abstract "); + } + #if DEBUG + if (c.HasCompoundClass) + builder.Append("multiple_parts "); + if (c is CompoundClass) { + builder.Append("compound{"); + builder.Append(string.Join(",", (c as CompoundClass).Parts.Select(p => p.CompilationUnit.FileName).ToArray())); + builder.Append("} "); + } + #endif + } + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (ShowDefinitionKeyWord) { + switch (c.ClassType) { + case ClassType.Delegate: + builder.Append("delegate"); + break; + case ClassType.Class: + case ClassType.Module: + builder.Append("class"); + break; + case ClassType.Struct: + builder.Append("struct"); + break; + case ClassType.Interface: + builder.Append("interface"); + break; + case ClassType.Enum: + builder.Append("enum"); + break; + } + builder.Append(' '); + } + if (ShowReturnType && c.ClassType == ClassType.Delegate) { + foreach(IMethod m in c.Methods) { + if (m.Name != "Invoke") continue; + + builder.Append(Convert(m.ReturnType)); + builder.Append(' '); + } + } + + AppendClassNameWithTypeParameters(builder, c, UseFullyQualifiedMemberNames, true, null); + + if (ShowParameterList && c.ClassType == ClassType.Delegate) { + builder.Append(" ("); + if (IncludeHtmlMarkup) builder.Append("
"); + + foreach(IMethod m in c.Methods) { + if (m.Name != "Invoke") continue; + + for (int i = 0; i < m.Parameters.Count; ++i) { + if (IncludeHtmlMarkup) builder.Append("   "); + + builder.Append(Convert(m.Parameters[i])); + if (i + 1 < m.Parameters.Count) builder.Append(", "); + + if (IncludeHtmlMarkup) builder.Append("
"); + } + } + builder.Append(')'); + + } else if (ShowInheritanceList) { + if (c.BaseTypes.Count > 0) { + builder.Append(" : "); + for (int i = 0; i < c.BaseTypes.Count; ++i) { + builder.Append(c.BaseTypes[i]); + if (i + 1 < c.BaseTypes.Count) { + builder.Append(", "); + } + } + } + } + + if (IncludeBody) { + builder.Append("\n{"); + } + + return builder.ToString(); + } + + void AppendClassNameWithTypeParameters(StringBuilder builder, IClass c, bool fullyQualified, bool isConvertingClassName, IList typeArguments) + { + if (isConvertingClassName && IncludeHtmlMarkup) { + builder.Append(""); + } + if (fullyQualified) { + if (c.DeclaringType != null) { + AppendClassNameWithTypeParameters(builder, c.DeclaringType, fullyQualified, false, typeArguments); + builder.Append('.'); + builder.Append(c.Name); + } else { + builder.Append(c.FullyQualifiedName); + } + } else { + builder.Append(c.Name); + } + if (isConvertingClassName && IncludeHtmlMarkup) { + builder.Append(""); + } + // skip type parameters that belong to declaring types (in DOM, inner classes repeat type parameters from outer classes) + int skippedTypeParameterCount = c.DeclaringType != null ? c.DeclaringType.TypeParameters.Count : 0; + // show type parameters for classes only if ShowTypeParameterList is set; but always show them in other cases. + if ((ShowTypeParameterList || !isConvertingClassName) && c.TypeParameters.Count > skippedTypeParameterCount) { + builder.Append('<'); + for (int i = skippedTypeParameterCount; i < c.TypeParameters.Count; ++i) { + if (i > skippedTypeParameterCount) + builder.Append(", "); + if (typeArguments != null && i < typeArguments.Count) + AppendReturnType(builder, typeArguments[i], false); + else + builder.Append(ConvertTypeParameter(c.TypeParameters[i])); + } + builder.Append('>'); + } + } + + public override string ConvertEnd(IClass c) + { + return "}"; + } + + public override string Convert(IField field) + { + CheckThread(); + + StringBuilder builder = new StringBuilder(); + + builder.Append(ConvertAccessibility(field.Modifiers)); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (ShowModifiers) { + if (field.IsConst) { + builder.Append("const "); + } else if (field.IsStatic) { + builder.Append("static "); + } + + if (field.IsNew) { + builder.Append("new "); + } + if (field.IsReadonly) { + builder.Append("readonly "); + } + if ((field.Modifiers & ModifierEnum.Volatile) == ModifierEnum.Volatile) { + builder.Append("volatile "); + } + } + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (field.ReturnType != null && ShowReturnType) { + builder.Append(Convert(field.ReturnType)); + builder.Append(' '); + } + + AppendTypeNameForFullyQualifiedMemberName(builder, field.DeclaringTypeReference); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + builder.Append(field.Name); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (IncludeBody) builder.Append(";"); + + return builder.ToString(); + } + + public override string Convert(IProperty property) + { + CheckThread(); + + StringBuilder builder = new StringBuilder(); + + builder.Append(ConvertAccessibility(property.Modifiers)); + + if (ShowModifiers) { + builder.Append(GetModifier(property)); + } + + if (property.ReturnType != null && ShowReturnType) { + builder.Append(Convert(property.ReturnType)); + builder.Append(' '); + } + + AppendTypeNameForFullyQualifiedMemberName(builder, property.DeclaringTypeReference); + + if (property.IsIndexer) { + builder.Append("this"); + } else { + if (IncludeHtmlMarkup) { + builder.Append(""); + } + builder.Append(property.Name); + if (IncludeHtmlMarkup) { + builder.Append(""); + } + } + + if (property.Parameters.Count > 0 && ShowParameterList) { + builder.Append(property.IsIndexer ? '[' : '('); + if (IncludeHtmlMarkup) builder.Append("
"); + + for (int i = 0; i < property.Parameters.Count; ++i) { + if (IncludeHtmlMarkup) builder.Append("   "); + builder.Append(Convert(property.Parameters[i])); + if (i + 1 < property.Parameters.Count) { + builder.Append(", "); + } + if (IncludeHtmlMarkup) builder.Append("
"); + } + + builder.Append(property.IsIndexer ? ']' : ')'); + } + + if (IncludeBody) { + builder.Append(" { "); + + if (property.CanGet) { + builder.Append("get; "); + } + if (property.CanSet) { + builder.Append("set; "); + } + + builder.Append(" } "); + } + + return builder.ToString(); + } + + public override string Convert(IEvent e) + { + CheckThread(); + + StringBuilder builder = new StringBuilder(); + + builder.Append(ConvertAccessibility(e.Modifiers)); + + if (ShowModifiers) { + builder.Append(GetModifier(e)); + } + + if (ShowDefinitionKeyWord) { + builder.Append("event "); + } + + if (e.ReturnType != null && ShowReturnType) { + builder.Append(Convert(e.ReturnType)); + builder.Append(' '); + } + + AppendTypeNameForFullyQualifiedMemberName(builder, e.DeclaringTypeReference); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + builder.Append(e.Name); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (IncludeBody) builder.Append(";"); + + return builder.ToString(); + } + + public override string Convert(IMethod m) + { + CheckThread(); + + StringBuilder builder = new StringBuilder(); + builder.Append(ConvertAccessibility(m.Modifiers)); + + if (ShowModifiers) { + builder.Append(GetModifier(m)); + } + + if (!m.IsConstructor && m.ReturnType != null && ShowReturnType) { + builder.Append(Convert(m.ReturnType)); + builder.Append(' '); + } + + AppendTypeNameForFullyQualifiedMemberName(builder, m.DeclaringTypeReference); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (m.IsConstructor && m.DeclaringType != null) { + builder.Append(m.DeclaringType.Name); + } else { + builder.Append(m.Name); + } + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (ShowTypeParameterList && m.TypeParameters.Count > 0) { + builder.Append('<'); + for (int i = 0; i < m.TypeParameters.Count; ++i) { + if (i > 0) builder.Append(", "); + builder.Append(ConvertTypeParameter(m.TypeParameters[i])); + } + builder.Append('>'); + } + + if (ShowParameterList) { + builder.Append("("); + if (IncludeHtmlMarkup) builder.Append("
"); + + if (m.IsExtensionMethod) builder.Append("this "); + + for (int i = 0; i < m.Parameters.Count; ++i) { + if (IncludeHtmlMarkup) builder.Append("   "); + builder.Append(Convert(m.Parameters[i])); + if (i + 1 < m.Parameters.Count) { + builder.Append(", "); + } + if (IncludeHtmlMarkup) builder.Append("
"); + } + + builder.Append(')'); + } + + if (IncludeBody) { + if (m.DeclaringType != null) { + if (m.DeclaringType.ClassType == ClassType.Interface) { + builder.Append(";"); + } else { + builder.Append(" {"); + } + } else { + builder.Append(" {"); + } + } + return builder.ToString(); + } + + void AppendTypeNameForFullyQualifiedMemberName(StringBuilder builder, IReturnType declaringType) + { + if (UseFullyQualifiedMemberNames && declaringType != null) { + AppendReturnType(builder, declaringType, true); + builder.Append('.'); + } + } + + string ConvertTypeParameter(ITypeParameter tp) + { + if (tp.BoundTo != null) + return Convert(tp.BoundTo); + else + return tp.Name; + } + + public override string ConvertEnd(IMethod m) + { + return "}"; + } + + public override string Convert(IReturnType returnType) + { + CheckThread(); + + if (returnType == null) { + return String.Empty; + } + + returnType = returnType.GetDirectReturnType(); + + StringBuilder builder = new StringBuilder(); + + AppendReturnType(builder, returnType, false); + + return builder.ToString(); + } + + void AppendReturnType(StringBuilder builder, IReturnType returnType, bool forceFullyQualifiedName) + { + IReturnType arrayReturnType = returnType; + returnType = GetElementType(returnType); + + if (returnType == null) + return; + + string fullName = returnType.FullyQualifiedName; + string shortName; + bool isConstructedType = returnType.IsConstructedReturnType; + if (fullName != null && !isConstructedType && TypeConversionTable.TryGetValue(fullName, out shortName)) { + builder.Append(shortName); + } else { + IClass c = returnType.GetUnderlyingClass(); + + if (c != null) { + IList ta = isConstructedType ? returnType.CastToConstructedReturnType().TypeArguments : null; + AppendClassNameWithTypeParameters(builder, c, forceFullyQualifiedName || UseFullyQualifiedTypeNames, false, ta); + } else { + if (UseFullyQualifiedTypeNames || forceFullyQualifiedName) { + builder.Append(fullName); + } else { + builder.Append(returnType.Name); + } + if (isConstructedType) { + builder.Append('<'); + IList ta = returnType.CastToConstructedReturnType().TypeArguments; + for (int i = 0; i < ta.Count; ++i) { + if (i > 0) builder.Append(", "); + AppendReturnType(builder, ta[i], false); + } + builder.Append('>'); + } + } + } + + UnpackArrayType(builder, arrayReturnType); + } + + static IReturnType GetElementType(IReturnType potentialArrayType) + { + if (potentialArrayType == null) + return null; + ArrayReturnType result; + while ((result = potentialArrayType.CastToArrayReturnType()) != null) { + potentialArrayType = result.ArrayElementType; + } + return potentialArrayType; + } + + static void UnpackArrayType(StringBuilder builder, IReturnType returnType) + { + if (returnType.IsArrayReturnType) { + builder.Append('['); + int dimensions = returnType.CastToArrayReturnType().ArrayDimensions; + for (int i = 1; i < dimensions; ++i) { + builder.Append(','); + } + builder.Append(']'); + UnpackArrayType(builder, returnType.CastToArrayReturnType().ArrayElementType); + } + } + + public override string Convert(IParameter param) + { + CheckThread(); + + StringBuilder builder = new StringBuilder(); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (param.IsRef) { + builder.Append("ref "); + } else if (param.IsOut) { + builder.Append("out "); + } else if (param.IsParams) { + builder.Append("params "); + } + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + builder.Append(Convert(param.ReturnType)); + + if (ShowParameterNames) { + builder.Append(' '); + builder.Append(param.Name); + } + return builder.ToString(); + } + + public override string WrapAttribute(string attribute) + { + return "[" + attribute + "]"; + } + + public override string WrapComment(string comment) + { + return "// " + comment; + } + + public override string GetIntrinsicTypeName(string dotNetTypeName) + { + string shortName; + if (TypeConversionTable.TryGetValue(dotNetTypeName, out shortName)) { + return shortName; + } + return dotNetTypeName; + } + + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/CSharpExpressionContext.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/CSharpExpressionContext.cs new file mode 100644 index 000000000..1dfaa0408 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/CSharpExpressionContext.cs @@ -0,0 +1,55 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.SharpDevelop.Dom; + +namespace ICSharpCode.SharpDevelop.Dom.CSharp +{ + public static class CSharpExpressionContext + { + /// The context is the body of a property declaration. + /// string Name { *expr* } + public readonly static ExpressionContext PropertyDeclaration = new ExpressionContext.DefaultExpressionContext("PropertyDeclaration"); + + /// The context is the body of a property declaration inside an interface. + /// string Name { *expr* } + public readonly static ExpressionContext InterfacePropertyDeclaration = new ExpressionContext.DefaultExpressionContext("InterfacePropertyDeclaration"); + + /// The context is the body of a event declaration. + /// event EventHandler NameChanged { *expr* } + public readonly static ExpressionContext EventDeclaration = new ExpressionContext.DefaultExpressionContext("EventDeclaration"); + + /// The context is after the type parameters of a type/method declaration. + /// The only valid keyword is "where" + /// class <T> *expr* + public readonly static ExpressionContext ConstraintsStart = new ExpressionContext.DefaultExpressionContext("ConstraintsStart"); + + /// The context is after the 'where' of a constraints list. + /// class <T> where *expr* + public readonly static ExpressionContext Constraints = new ExpressionContext.NonStaticTypeExpressionContext("Constraints", false); + + /// The context is the body of an interface declaration. + public readonly static ExpressionContext InterfaceDeclaration = new ExpressionContext.NonStaticTypeExpressionContext("InterfaceDeclaration", true); + + /// Context expects "base" or "this". + /// public ClassName() : *expr* + public readonly static ExpressionContext BaseConstructorCall = new ExpressionContext.DefaultExpressionContext("BaseConstructorCall"); + + /// The first parameter + /// void MethodName(*expr*) + public readonly static ExpressionContext FirstParameterType = new ExpressionContext.NonStaticTypeExpressionContext("FirstParameterType", false); + + /// Another parameter + /// void MethodName(..., *expr*) + public readonly static ExpressionContext ParameterType = new ExpressionContext.NonStaticTypeExpressionContext("ParameterType", false); + + /// Context expects a fully qualified type name. + /// using Alias = *expr*; + public readonly static ExpressionContext FullyQualifiedType = new ExpressionContext.DefaultExpressionContext("FullyQualifiedType"); + + /// Context expects is a property name in an object initializer. + /// new *type* [(args)] { *expr* = ... } + public readonly static ExpressionContext ObjectInitializer = new ExpressionContext.DefaultExpressionContext("ObjectInitializer"); + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/ExpressionFinder.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/ExpressionFinder.cs new file mode 100644 index 000000000..b7f187599 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/ExpressionFinder.cs @@ -0,0 +1,1057 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Parser; +using ICSharpCode.NRefactory.Parser.CSharp; + +namespace ICSharpCode.SharpDevelop.Dom.CSharp +{ + /// + /// Supports getting the expression including context from the cursor position. + /// + public class CSharpExpressionFinder : IExpressionFinder + { + ParseInformation parseInformation; + IProjectContent projectContent; + + public CSharpExpressionFinder(ParseInformation parseInformation) + { + this.parseInformation = parseInformation; + if (parseInformation != null && parseInformation.CompilationUnit != null) { + projectContent = parseInformation.CompilationUnit.ProjectContent; + } else { + projectContent = DefaultProjectContent.DummyProjectContent; + } + } + + ILexer lexer; + Location targetPosition; + List lineOffsets; + + int LocationToOffset(Location location) + { + if (location.Line <= 0) return -1; + return lineOffsets[location.Line - 1] + location.Column - 1; + } + + Location OffsetToLocation(int offset) + { + int lineNumber = lineOffsets.BinarySearch(offset); + if (lineNumber < 0) { + lineNumber = (~lineNumber) - 1; + } + return new Location(offset - lineOffsets[lineNumber] + 1, lineNumber + 1); + } + + enum FrameType + { + Global, + /// + /// "class C { * }" + /// + TypeDecl, + /// + /// "interface I { * }" + /// + Interface, + /// + /// "enum E { * }" + /// + Enum, + /// + /// "void Method(*) {}" + /// + ParameterList, + /// + /// "public string Property { * }" + /// + Property, + /// + /// "public event EventHandler SomethingChanged { * }" + /// + Event, + /// + /// "void Method() { * }" + /// + Statements, + /// + /// "if (*) {}" + /// + Expression, + /// + /// "new T { * }" + /// + ObjectInitializer, + /// + /// "[*] class ..." + /// + AttributeSection, + /// + /// "[Obsolete(*)] class ..." + /// + AttributeArguments, + /// + /// Type reference frame "typeof(*)" + /// + TypeReference, + /// + /// Type parameter declaration, "class C<*gt;" + /// + TypeParameterDecl, + /// + /// The Frame is no longer active. + /// + Popped + } + + /// + /// Used to support a frame-type specific state machine. Used in TrackCurrentContext + /// + enum FrameState + { + /// + /// the default state (all frame types) + /// + Normal, + /// + /// parsing an inheritance list (Global+TypeDecl) + /// + InheritanceList, + /// + /// parsing an event declaration (Interface+TypeDecl) + /// + EventDecl, + /// + /// parsing a field declaration (Interface+TypeDecl). + /// Could also be a method or property declaration. + /// + FieldDecl, + /// + /// parsing a field declaration, the field name was already specified (Interface+TypeDecl). + /// Could also be a method or property declaration. + /// + FieldDeclAfterIdentifier, + /// + /// parsing a method declaration (Interface+TypeDecl) or a delegate declaration (Global+TypeDecl) + /// + MethodDecl, + /// + /// parsing a field initializer (TypeDecl) + /// + Initializer, + /// + /// Between class/struct/enum keyword and body of the type declaration + /// + TypeDecl, + /// + /// Between "where" and start of the generic method/class + /// + Constraints, + /// + /// Between "new" and "(" / "{". + /// + ObjectCreation, + /// + /// In object initializer, in the value part (after "=") + /// + ObjectInitializerValue, + /// + /// After a keyword like "if","while","using" etc., but before the embedded statement. + /// + StatementWithEmbeddedStatement, + /// + /// After "using" in global context, but before "=" or ";". + /// + UsingNamespace + } + + /// + /// When parsing the code, each block starting with one of the brackets "(", "[", "{" or "<" (for generics) + /// gets an instance of Frame. + /// + sealed class Frame + { + internal Frame parent; + internal FrameType type; + internal FrameType parenthesisChildType; + internal FrameType curlyChildType; + internal FrameType squareBracketChildType; + internal FrameState state; + internal char bracketType; + internal ExpressionContext context; + internal IReturnType expectedType; + + internal bool InExpressionMode { + get { + return type == FrameType.Statements + || type == FrameType.Expression + || type == FrameType.AttributeArguments + || type == FrameType.ObjectInitializer + || state == FrameState.Initializer + || state == FrameState.ObjectCreation; + } + } + + internal void SetContext(ExpressionContext context) + { + this.context = context; + this.expectedType = null; + } + internal void SetExpectedType(IReturnType expectedType) + { + this.expectedType = expectedType; + this.context = ExpressionContext.Default; + } + internal void SetDefaultContext() + { + if (state == FrameState.InheritanceList) { + if (curlyChildType == FrameType.Enum) { + SetContext(ExpressionContext.EnumBaseType); + } else if (curlyChildType == FrameType.Interface) { + SetContext(ExpressionContext.Interface); + } else { + SetContext(ExpressionContext.InheritableType); + } + } else if (state == FrameState.Constraints) { + SetContext(CSharpExpressionContext.Constraints); + } else if (state == FrameState.UsingNamespace) { + SetContext(ExpressionContext.Namespace); + } else { + switch (type) { + case FrameType.Global: + SetContext(ExpressionContext.Global); + break; + case FrameType.TypeDecl: + SetContext(ExpressionContext.TypeDeclaration); + break; + case FrameType.Enum: + case FrameType.TypeParameterDecl: + SetContext(ExpressionContext.IdentifierExpected); + break; + case FrameType.Interface: + SetContext(CSharpExpressionContext.InterfaceDeclaration); + break; + case FrameType.Event: + SetContext(CSharpExpressionContext.EventDeclaration); + break; + case FrameType.Property: + if (parent != null && parent.type == FrameType.Interface) { + SetContext(CSharpExpressionContext.InterfacePropertyDeclaration); + } else { + SetContext(CSharpExpressionContext.PropertyDeclaration); + } + break; + case FrameType.Statements: + SetContext(ExpressionContext.MethodBody); + break; + case FrameType.ParameterList: + SetContext(CSharpExpressionContext.ParameterType); + break; + case FrameType.ObjectInitializer: + if (state == FrameState.ObjectInitializerValue) { + SetContext(ExpressionContext.Default); + } else { + SetContext(CSharpExpressionContext.ObjectInitializer); + } + break; + case FrameType.AttributeSection: + SetContext(ExpressionContext.Attribute); + break; + case FrameType.TypeReference: + SetContext(ExpressionContext.Type); + break; + default: + SetContext(ExpressionContext.Default); + break; + } + } + } + + /// start of the expression currently being tracked + internal Location lastExpressionStart; + + /// Position of the last "new" keyword + internal Location lastNewTokenStart; + + public Frame() : this(null, '\0') {} + + public Frame(Frame parent, char bracketType) + { + this.parent = parent; + this.bracketType = bracketType; + if (parent != null) { + if (bracketType == '{') { + this.type = parent.curlyChildType; + } else if (bracketType == '(') { + this.type = parent.parenthesisChildType; + } else if (bracketType == '[') { + this.type = parent.squareBracketChildType; + } else { + this.type = parent.type; + } + } + ResetCurlyChildType(); + ResetParenthesisChildType(); + ResetSquareBracketChildType(); + SetDefaultContext(); + } + + public void ResetCurlyChildType() + { + if (state == FrameState.Initializer) { + this.curlyChildType = FrameType.Expression; + } else { + switch (this.type) { + case FrameType.Property: + case FrameType.Event: + this.curlyChildType = FrameType.Statements; + break; + default: + this.curlyChildType = this.type; + break; + } + } + } + + public void ResetParenthesisChildType() + { + if (this.InExpressionMode) { + this.parenthesisChildType = FrameType.Expression; + } else if (type == FrameType.AttributeSection) { + this.parenthesisChildType = FrameType.AttributeArguments; + } else { + this.parenthesisChildType = this.type; + } + } + + public void ResetSquareBracketChildType() + { + if (InExpressionMode) + this.squareBracketChildType = FrameType.Expression; + else + this.squareBracketChildType = FrameType.AttributeSection; + } + } + + void Init(string text, int offset) + { + if (offset < 0 || offset > text.Length) + throw new ArgumentOutOfRangeException("offset", offset, "offset must be between 0 and " + text.Length); + lexer = ParserFactory.CreateLexer(SupportedLanguage.CSharp, new StringReader(text)); + lexer.SkipAllComments = true; + lineOffsets = new List(); + lineOffsets.Add(0); + for (int i = 0; i < text.Length; i++) { + if (i == offset) { + targetPosition = new Location(offset - lineOffsets[lineOffsets.Count - 1] + 1, lineOffsets.Count); + } + if (text[i] == '\n') { + lineOffsets.Add(i + 1); + } else if (text[i] == '\r') { + if (i + 1 < text.Length && text[i + 1] != '\n') { + lineOffsets.Add(i + 1); + } + } + } + if (offset == text.Length) { + targetPosition = new Location(offset - lineOffsets[lineOffsets.Count - 1] + 1, lineOffsets.Count); + } + + frame = new Frame(); + lastToken = Tokens.EOF; + } + + Frame frame; + int lastToken; + + public ExpressionResult FindExpression(string text, int offset) + { + Init(text, offset); + Token token; + Location lastError = Location.Empty; + lexer.Errors.Error = delegate (int errorLine, int errorCol, string errorMsg) { + lastError = new Location(errorCol, errorLine); + }; + while ((token = lexer.NextToken()) != null) { + if (token.Kind == Tokens.EOF) break; + + if (targetPosition <= token.Location) { + break; + } + ApplyToken(token); + if (targetPosition <= token.EndLocation) { + if (token.Kind == Tokens.Literal) { + // do not return string literal as expression if offset was inside the literal, + // or if it was at the end of the literal when the literal was not terminated correctly. + if (targetPosition < token.EndLocation || lastError == token.Location) { + frame.lastExpressionStart = Location.Empty; + } + } + break; + } + lastToken = token.Kind; + } + + int tokenOffset; + if (token == null || token.Kind == Tokens.EOF) + tokenOffset = text.Length; + else + tokenOffset = LocationToOffset(token.Location); + int lastExpressionStartOffset = LocationToOffset(frame.lastExpressionStart); + if (lastExpressionStartOffset >= 0) { + if (offset < tokenOffset) { + // offset is in front of this token + return MakeResult(text, lastExpressionStartOffset, tokenOffset, frame.context); + } else { + // offset is IN this token + return MakeResult(text, lastExpressionStartOffset, offset, frame.context); + } + } else { + return new ExpressionResult(null, frame.context); + } + } + + void ApplyToken(Token token) + { + TrackCurrentFrameAndExpression(token); + TrackCurrentContext(token); + } + + void TrackCurrentFrameAndExpression(Token token) + { + while (frame.bracketType == '<' && !Tokens.ValidInsideTypeName[token.Kind]) { + frame.type = FrameType.Popped; + frame = frame.parent; + } + switch (token.Kind) { + case Tokens.OpenCurlyBrace: + frame.lastExpressionStart = Location.Empty; + frame = new Frame(frame, '{'); + frame.parent.ResetCurlyChildType(); + break; + case Tokens.CloseCurlyBrace: + while (frame.parent != null) { + if (frame.bracketType == '{') { + frame.type = FrameType.Popped; + frame = frame.parent; + break; + } else { + frame.type = FrameType.Popped; + frame = frame.parent; + } + } + break; + case Tokens.OpenParenthesis: + if (frame.lastExpressionStart.IsEmpty) + frame.lastExpressionStart = token.Location; + frame = new Frame(frame, '('); + frame.parent.ResetParenthesisChildType(); + break; + case Tokens.OpenSquareBracket: + frame = new Frame(frame, '['); + frame.parent.ResetSquareBracketChildType(); + break; + case Tokens.CloseParenthesis: + case Tokens.CloseSquareBracket: + if (frame.parent != null && (frame.bracketType == '(' || frame.bracketType == '[')) { + frame.type = FrameType.Popped; + frame = frame.parent; + } + break; + case Tokens.LessThan: + if (Tokens.ValidInsideTypeName[lastToken]) { + frame = new Frame(frame, '<'); + if (frame.parent.InExpressionMode) { + frame.SetContext(ExpressionContext.Default); + } else if ((frame.parent.state == FrameState.TypeDecl + || frame.parent.state == FrameState.MethodDecl + || frame.parent.state == FrameState.FieldDeclAfterIdentifier) + && frame.parent.context == ExpressionContext.IdentifierExpected) + { + frame.type = FrameType.TypeParameterDecl; + frame.SetContext(ExpressionContext.IdentifierExpected); + frame.parent.SetContext(CSharpExpressionContext.ConstraintsStart); + } else { + frame.SetContext(ExpressionContext.Type); + } + } + break; + case Tokens.GreaterThan: + if (frame.parent != null && frame.bracketType == '<') { + frame.type = FrameType.Popped; + frame = frame.parent; + } else { + frame.lastExpressionStart = Location.Empty; + frame.SetDefaultContext(); + } + break; + case Tokens.Question: + // do not reset context - TrackCurrentContext will take care of this + frame.lastExpressionStart = Location.Empty; + break; + case Tokens.Pointer: + case Tokens.Dot: + case Tokens.DoubleColon: + // let the current expression continue + break; + default: + if (Tokens.IdentifierTokens[token.Kind]) { + if (lastToken != Tokens.Dot && lastToken != Tokens.DoubleColon && lastToken != Tokens.Pointer) { + if (Tokens.ValidInsideTypeName[lastToken]) { + frame.SetDefaultContext(); + } + frame.lastExpressionStart = token.Location; + } + } else if (Tokens.SimpleTypeName[token.Kind] || Tokens.ExpressionStart[token.Kind] || token.Kind == Tokens.Literal) { + frame.lastExpressionStart = token.Location; + } else { + frame.lastExpressionStart = Location.Empty; + frame.SetDefaultContext(); + } + break; + } + } + + void TrackCurrentContext(Token token) + { + if (frame.state == FrameState.ObjectCreation) { + if (token.Kind == Tokens.CloseParenthesis) { + if (frame.context.IsObjectCreation) { + frame.context = ExpressionContext.Default; + frame.lastExpressionStart = frame.lastNewTokenStart; + } + // keep frame.state + } else if (token.Kind == Tokens.GreaterThan + || token.Kind == Tokens.DoubleColon || token.Kind == Tokens.Dot + || Tokens.SimpleTypeName[token.Kind]) + { + // keep frame.state == FrameState.ObjectCreationInType + } else { + frame.state = FrameState.Normal; + frame.ResetCurlyChildType(); + } + } else if (frame.state == FrameState.UsingNamespace) { + if (token.Kind != Tokens.Identifier && token.Kind != Tokens.Dot && token.Kind != Tokens.DoubleColon) { + frame.state = FrameState.Normal; + frame.SetDefaultContext(); + } + } + + switch (token.Kind) { + case Tokens.Using: + if (frame.type == FrameType.Global) { + frame.state = FrameState.UsingNamespace; + frame.SetDefaultContext(); + break; + } else { + goto case Tokens.For; + } + case Tokens.For: + case Tokens.Foreach: + case Tokens.Fixed: + case Tokens.Catch: + if (frame.type == FrameType.Statements) { + frame.parenthesisChildType = FrameType.Statements; + frame.state = FrameState.StatementWithEmbeddedStatement; + } + break; + case Tokens.If: + case Tokens.While: + case Tokens.Switch: + case Tokens.Lock: + if (frame.type == FrameType.Statements) { + frame.state = FrameState.StatementWithEmbeddedStatement; + } + break; + case Tokens.Throw: + frame.SetExpectedType(projectContent.SystemTypes.Exception); + break; + case Tokens.New: + if (frame.InExpressionMode) { + frame.SetContext(ExpressionContext.TypeDerivingFrom(frame.expectedType, true)); + frame.state = FrameState.ObjectCreation; + frame.curlyChildType = FrameType.ObjectInitializer; + frame.lastNewTokenStart = token.Location; + } + break; + case Tokens.Namespace: + frame.SetContext(ExpressionContext.IdentifierExpected); + break; + case Tokens.Assign: + if (frame.type == FrameType.Global) { + frame.SetContext(CSharpExpressionContext.FullyQualifiedType); + break; + } else if (frame.type == FrameType.Enum) { + frame.SetContext(ExpressionContext.Default); + break; + } else if (frame.type == FrameType.TypeDecl) { + frame.SetContext(ExpressionContext.Default); + frame.state = FrameState.Initializer; + frame.ResetParenthesisChildType(); + frame.ResetSquareBracketChildType(); + frame.ResetCurlyChildType(); + break; + } else if (frame.type == FrameType.ObjectInitializer) { + frame.state = FrameState.ObjectInitializerValue; + frame.SetDefaultContext(); + break; + } else { + goto default; + } + case Tokens.Colon: + if (frame.state == FrameState.MethodDecl && lastToken == Tokens.CloseParenthesis) { + frame.SetContext(CSharpExpressionContext.BaseConstructorCall); + frame.parenthesisChildType = FrameType.Expression; + } else { + if (frame.curlyChildType == FrameType.TypeDecl || frame.curlyChildType == FrameType.Interface || frame.curlyChildType == FrameType.Enum) { + if (frame.state != FrameState.Constraints) { + frame.state = FrameState.InheritanceList; + frame.SetDefaultContext(); + } + } + } + break; + case Tokens.Class: + case Tokens.Struct: + if (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl) { + if (frame.state != FrameState.Constraints) { + frame.state = FrameState.TypeDecl; + frame.curlyChildType = FrameType.TypeDecl; + frame.SetContext(ExpressionContext.IdentifierExpected); + } + } + break; + case Tokens.Interface: + if (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl) { + frame.state = FrameState.TypeDecl; + frame.curlyChildType = FrameType.Interface; + frame.SetContext(ExpressionContext.IdentifierExpected); + } + break; + case Tokens.Enum: + if (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl) { + frame.state = FrameState.TypeDecl; + frame.curlyChildType = FrameType.Enum; + frame.SetContext(ExpressionContext.IdentifierExpected); + } + break; + case Tokens.Delegate: + if (frame.InExpressionMode) { + frame.parenthesisChildType = FrameType.ParameterList; + frame.curlyChildType = FrameType.Statements; + } else if (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl) { + frame.parenthesisChildType = FrameType.ParameterList; + frame.state = FrameState.MethodDecl; + frame.SetContext(ExpressionContext.Type); + } + break; + case Tokens.LambdaArrow: + frame.curlyChildType = FrameType.Statements; + break; + case Tokens.Event: + frame.SetContext(ExpressionContext.DelegateType); + frame.curlyChildType = FrameType.Event; + frame.state = FrameState.EventDecl; + break; + case Tokens.Comma: + if (frame.state == FrameState.FieldDecl + || frame.state == FrameState.FieldDeclAfterIdentifier + || frame.state == FrameState.Initializer) + { + frame.state = FrameState.FieldDecl; + frame.SetContext(ExpressionContext.IdentifierExpected); + } else if (frame.state == FrameState.ObjectInitializerValue) { + frame.state = FrameState.Normal; + frame.SetDefaultContext(); + } else if (frame.type == FrameType.Statements) { + frame.SetContext(ExpressionContext.IdentifierExpected); + } + break; + case Tokens.Where: + if (!frame.InExpressionMode && (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl)) { + frame.state = FrameState.Constraints; + frame.SetDefaultContext(); + } + break; + case Tokens.CloseCurlyBrace: + case Tokens.Semicolon: + frame.state = FrameState.Normal; + frame.SetDefaultContext(); + break; + case Tokens.OpenParenthesis: + if (frame.parent != null && (frame.parent.state == FrameState.FieldDeclAfterIdentifier + || frame.parent.state == FrameState.FieldDecl)) + { + frame.type = FrameType.ParameterList; + frame.SetContext(CSharpExpressionContext.FirstParameterType); + frame.parent.state = FrameState.MethodDecl; + frame.parent.curlyChildType = FrameType.Statements; + } + break; + case Tokens.CloseParenthesis: + if (frame.state == FrameState.StatementWithEmbeddedStatement) { + frame.state = FrameState.Normal; + frame.lastExpressionStart = token.EndLocation; + } + break; + case Tokens.Question: + // IdentifierExpected = this is after a type name = the ? was a nullable marker + if (frame.context != ExpressionContext.IdentifierExpected) { + frame.SetDefaultContext(); + } + break; + case Tokens.This: + if (frame.state == FrameState.FieldDecl) { + // this is an indexer declaration + frame.squareBracketChildType = FrameType.ParameterList; + frame.state = FrameState.FieldDeclAfterIdentifier; + } + break; + case Tokens.Goto: + frame.SetContext(ExpressionContext.IdentifierExpected); + break; + case Tokens.As: + case Tokens.Is: + frame.SetContext(ExpressionContext.Type); + break; + case Tokens.Typeof: + frame.parenthesisChildType = FrameType.TypeReference; + break; + default: + if (Tokens.SimpleTypeName[token.Kind]) { + if (frame.type == FrameType.Interface || frame.type == FrameType.TypeDecl) { + if (frame.state == FrameState.Normal) { + frame.state = FrameState.FieldDecl; + frame.curlyChildType = FrameType.Property; + } else if (frame.state == FrameState.FieldDecl && Tokens.IdentifierTokens[token.Kind]) { + frame.state = FrameState.FieldDeclAfterIdentifier; + } + if (frame.state != FrameState.ObjectCreation) { + frame.SetContext(ExpressionContext.IdentifierExpected); + } + } else if (frame.type == FrameType.ParameterList + || frame.type == FrameType.Statements + || frame.type == FrameType.Global) + { + if (!frame.context.IsObjectCreation && frame.state != FrameState.UsingNamespace) { + frame.SetContext(ExpressionContext.IdentifierExpected); + } + } + } + break; + } + } + + public ExpressionResult FindFullExpression(string text, int offset) + { + return FindFullExpression(text, offset, null); + } + + /// + /// Like FindFullExpression, but text is a code snippet inside a type declaration. + /// + public ExpressionResult FindFullExpressionInTypeDeclaration(string text, int offset) + { + Frame root = new Frame(); + root.curlyChildType = FrameType.TypeDecl; + Frame typeDecl = new Frame(root, '{'); + return FindFullExpression(text, offset, typeDecl); + } + + + /// + /// Like FindFullExpression, but text is a code snippet inside a method body. + /// + public ExpressionResult FindFullExpressionInMethod(string text, int offset) + { + Frame root = new Frame(); + root.curlyChildType = FrameType.TypeDecl; + Frame typeDecl = new Frame(root, '{'); + typeDecl.curlyChildType = FrameType.Statements; + Frame methodBody = new Frame(typeDecl, '{'); + return FindFullExpression(text, offset, methodBody); + } + + ExpressionResult FindFullExpression(string text, int offset, Frame initialFrame) + { + Init(text, offset); + + if (initialFrame != null) { + frame = initialFrame; + } + + const int SEARCHING_OFFSET = 0; + const int SEARCHING_END = 1; + int state = SEARCHING_OFFSET; + Frame resultFrame = frame; + int resultStartOffset = -1; + int alternateResultStartOffset = -1; + int resultEndOffset = -1; + ExpressionContext prevContext = ExpressionContext.Default; + ExpressionContext resultContext = ExpressionContext.Default; + + Token token; + while ((token = lexer.NextToken()) != null) { + if (token.Kind == Tokens.EOF) break; + + if (state == SEARCHING_OFFSET) { + if (targetPosition < token.Location) { + resultFrame = frame; + resultContext = frame.context; + resultStartOffset = LocationToOffset(frame.lastExpressionStart); + alternateResultStartOffset = LocationToOffset(frame.lastNewTokenStart); + if (resultStartOffset < 0) + break; + resultEndOffset = LocationToOffset(token.Location); + state = SEARCHING_END; + } + } + prevContext = frame.context; + ApplyToken(token); + if (state == SEARCHING_OFFSET) { + if (targetPosition < token.EndLocation) { + resultFrame = frame; + resultContext = prevContext; + resultStartOffset = LocationToOffset(frame.lastExpressionStart); + alternateResultStartOffset = LocationToOffset(frame.lastNewTokenStart); + resultEndOffset = LocationToOffset(token.EndLocation); + if (resultStartOffset < 0) + break; + state = SEARCHING_END; + } + } else if (state == SEARCHING_END) { + int lastExpressionStartOffset = LocationToOffset(resultFrame.lastExpressionStart); + if (lastExpressionStartOffset == alternateResultStartOffset && alternateResultStartOffset >= 0) + resultStartOffset = lastExpressionStartOffset; + if (resultFrame.type == FrameType.Popped || + lastExpressionStartOffset != resultStartOffset || + token.Kind == Tokens.Dot || token.Kind == Tokens.DoubleColon || token.Kind == Tokens.Pointer) + { + + // now we can change the context based on the next token + if (frame == resultFrame && Tokens.IdentifierTokens[token.Kind]) { + // the expression got aborted because of an identifier. This means the + // expression was a type reference + resultContext = ExpressionContext.Type; + } else if (resultFrame.bracketType == '<' && token.Kind == Tokens.GreaterThan) { + // expression was a type argument + resultContext = ExpressionContext.Type; + return MakeResult(text, resultStartOffset, resultEndOffset, resultContext); + } + if (frame == resultFrame || resultFrame.type == FrameType.Popped) { + return MakeResult(text, resultStartOffset, resultEndOffset, resultContext); + } + } else { + if (frame.bracketType != '<') { + resultEndOffset = LocationToOffset(token.EndLocation); + } + } + } + lastToken = token.Kind; + } + // offset is behind all tokens -> cannot find any expression + return new ExpressionResult(null, frame.context); + } + + ExpressionResult MakeResult(string text, int startOffset, int endOffset, ExpressionContext context) + { + return new ExpressionResult(text.Substring(startOffset, endOffset - startOffset), + DomRegion.FromLocation(OffsetToLocation(startOffset), OffsetToLocation(endOffset)), + context, null); + } + + public string RemoveLastPart(string expression) + { + Init(expression, expression.Length - 1); + int lastValidPos = 0; + Token token; + while ((token = lexer.NextToken()) != null) { + if (token.Kind == Tokens.EOF) break; + + if (frame.parent == null) { + if (token.Kind == Tokens.Dot || token.Kind == Tokens.DoubleColon || token.Kind == Tokens.Pointer + || token.Kind == Tokens.OpenParenthesis || token.Kind == Tokens.OpenSquareBracket) + { + lastValidPos = LocationToOffset(token.Location); + } + } + ApplyToken(token); + + lastToken = token.Kind; + } + return expression.Substring(0, lastValidPos); + } + + #region Comment Filter and 'inside string watcher' + + // NOTE: FilterComments is not used anymore inside the ExpressionFinder, it should be moved + // into another class / or removed completely if it is not required anymore. + + int initialOffset; + public string FilterComments(string text, ref int offset) + { + if (text.Length <= offset) + return null; + this.initialOffset = offset; + StringBuilder outText = new StringBuilder(); + int curOffset = 0; + + while (curOffset <= initialOffset) { + char ch = text[curOffset]; + + switch (ch) { + case '@': + if (curOffset + 1 < text.Length && text[curOffset + 1] == '"') { + outText.Append(text[curOffset++]); // @ + outText.Append(text[curOffset++]); // " + if (!ReadVerbatimString(outText, text, ref curOffset)) { + return null; + } + }else{ + outText.Append(ch); + ++curOffset; + } + break; + case '\'': + outText.Append(ch); + curOffset++; + if(! ReadChar(outText, text, ref curOffset)) { + return null; + } + break; + case '"': + outText.Append(ch); + curOffset++; + if (!ReadString(outText, text, ref curOffset)) { + return null; + } + break; + case '/': + if (curOffset + 1 < text.Length && text[curOffset + 1] == '/') { + offset -= 2; + curOffset += 2; + if (!ReadToEOL(text, ref curOffset, ref offset)) { + return null; + } + } else if (curOffset + 1 < text.Length && text[curOffset + 1] == '*') { + offset -= 2; + curOffset += 2; + if (!ReadMultiLineComment(text, ref curOffset, ref offset)) { + return null; + } + } else { + goto default; + } + break; + case '#': + if (!ReadToEOL(text, ref curOffset, ref offset)) { + return null; + } + break; + default: + outText.Append(ch); + ++curOffset; + break; + } + + + + } + + return outText.ToString(); + + } + + bool ReadToEOL(string text, ref int curOffset, ref int offset) + { + while (curOffset <= initialOffset) { + char ch = text[curOffset++]; + --offset; + if (ch == '\n') { + return true; + } + } + return false; + } + + bool ReadChar(StringBuilder outText, string text, ref int curOffset) + { + if (curOffset > initialOffset) + return false; + char first = text[curOffset++]; + outText.Append(first); + if (curOffset > initialOffset) + return false; + char second = text[curOffset++]; + outText.Append(second); + if (first == '\\') { + // character is escape sequence, so read one char more + char next; + do { + if (curOffset > initialOffset) + return false; + next = text[curOffset++]; + outText.Append(next); + // unicode or hexadecimal character literals can have more content characters + } while((second == 'u' || second == 'x') && char.IsLetterOrDigit(next)); + } + return text[curOffset - 1] == '\''; + } + + bool ReadString(StringBuilder outText, string text, ref int curOffset) + { + while (curOffset <= initialOffset) { + char ch = text[curOffset++]; + outText.Append(ch); + if (ch == '"') { + return true; + } else if (ch == '\\') { + if (curOffset <= initialOffset) + outText.Append(text[curOffset++]); + } + } + return false; + } + + bool ReadVerbatimString(StringBuilder outText, string text, ref int curOffset) + { + while (curOffset <= initialOffset) { + char ch = text[curOffset++]; + outText.Append(ch); + if (ch == '"') { + if (curOffset < text.Length && text[curOffset] == '"') { + outText.Append(text[curOffset++]); + } else { + return true; + } + } + + + } + return false; + + } + + bool ReadMultiLineComment(string text, ref int curOffset, ref int offset) + { + while (curOffset <= initialOffset) { + char ch = text[curOffset++]; + --offset; + if (ch == '*') { + if (curOffset < text.Length && text[curOffset] == '/') { + ++curOffset; + --offset; + return true; + } + } + } + return false; + } + #endregion + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/OverloadResolution.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/OverloadResolution.cs new file mode 100644 index 000000000..8964fa9cb --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/OverloadResolution.cs @@ -0,0 +1,340 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Linq; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom.CSharp +{ + /// + /// Implements C# 3.0 overload resolution. + /// + public sealed class OverloadResolution + { + private OverloadResolution() {} + + public static IMethodOrProperty FindOverload(IEnumerable list, + IReturnType[] arguments, + bool allowAdditionalArguments, + bool substituteInferredTypes, + out bool acceptableMatch) + { + OverloadResolution or = new OverloadResolution(); + or.candidates = list.Select(m => new Candidate(m)).ToList(); + or.arguments = arguments; + or.allowAdditionalArguments = allowAdditionalArguments; + + if (or.candidates.Count == 0) + throw new ArgumentException("at least one candidate is required"); + + MemberLookupHelper.Log("OverloadResolution"); + MemberLookupHelper.Log(" arguments = ", arguments); + + or.ConstructExpandedForms(); + or.InferTypeArguments(); + or.CheckApplicability(); + + Candidate result = or.FindBestCandidate(); + MemberLookupHelper.Log("Overload resolution finished. Winning candidate = " + result); + acceptableMatch = result.Status == CandidateStatus.Success; + if (substituteInferredTypes) + return result.Method; + else + return result.OriginalMethod; + } + + enum CandidateStatus + { + Success, + WrongParameterCount, + TypeInferenceFailed, + NotApplicable + } + + sealed class Candidate + { + public bool IsExpanded; + public IMethodOrProperty Method; + public IMethodOrProperty OriginalMethod; + public CandidateStatus Status = CandidateStatus.Success; + public int ApplicableArgumentCount; + + public IList Parameters { + get { return Method.Parameters; } + } + + public int TypeParameterCount { + get { + IMethod m = Method as IMethod; + if (m != null) + return m.TypeParameters.Count; + else + return 0; + } + } + + public Candidate(IMethodOrProperty method) + { + if (method == null) + throw new ArgumentNullException("method"); + this.Method = method; + this.OriginalMethod = method; + } + + public override string ToString() + { + return "[Candidate: " + Method + ", Status=" + Status + "]"; + } + } + + List candidates; + IList arguments; + bool allowAdditionalArguments; + + /// + /// For methods having a params-array as last parameter, expand the params array to + /// n parameters of the element type and add those as new candidates. + /// Mark candidates with the wrong parameter count as Status.WrongParameterCount. + /// + void ConstructExpandedForms() + { + LogStep("Step 1 (Construct expanded forms)"); + foreach (Candidate candidate in candidates.ToArray()) { + if (candidate.Status == CandidateStatus.Success) { + if (candidate.Parameters.Count > 0 && arguments.Count >= candidate.Parameters.Count - 1) { + IParameter lastParameter = candidate.Parameters[candidate.Parameters.Count - 1]; + if (lastParameter.IsParams && lastParameter.ReturnType.IsArrayReturnType) { + // try to construct an expanded form with the correct parameter count + IReturnType elementType = lastParameter.ReturnType.CastToArrayReturnType().ArrayElementType; + IMethodOrProperty expanded = (IMethodOrProperty)candidate.Method.CreateSpecializedMember(); + expanded.Parameters.RemoveAt(candidate.Parameters.Count - 1); + int index = 0; + while (expanded.Parameters.Count < arguments.Count) { + expanded.Parameters.Add(new DefaultParameter(lastParameter.Name + (index++), elementType, lastParameter.Region)); + } + candidates.Add(new Candidate(expanded) { + IsExpanded = true, + OriginalMethod = candidate.Method + }); + } + } + + if (allowAdditionalArguments) { + if (candidate.Parameters.Count < arguments.Count) { + candidate.Status = CandidateStatus.WrongParameterCount; + } + } else { + if (candidate.Parameters.Count != arguments.Count) { + candidate.Status = CandidateStatus.WrongParameterCount; + } + } + } + } + } + + /// + /// Infer the type arguments for generic methods. + /// + void InferTypeArguments() + { + LogStep("Step 2 (Infer type arguments)"); + foreach (Candidate candidate in candidates) { + IMethod method = candidate.Method as IMethod; + if (method != null && method.TypeParameters.Count > 0 + && candidate.Status == CandidateStatus.Success) + { + bool success; + IReturnType[] typeArguments = TypeInference.InferTypeArguments(method, arguments, out success); + if (!success) { + candidate.Status = CandidateStatus.TypeInferenceFailed; + } + candidate.Method = ApplyTypeArgumentsToMethod(method, typeArguments); + } + } + } + + static IMethod ApplyTypeArgumentsToMethod(IMethod genericMethod, IList typeArguments) + { + if (typeArguments != null && typeArguments.Count > 0) { + // apply inferred type arguments + IMethod method = (IMethod)genericMethod.CreateSpecializedMember(); + method.ReturnType = ConstructedReturnType.TranslateType(method.ReturnType, typeArguments, true); + for (int i = 0; i < method.Parameters.Count; ++i) { + method.Parameters[i].ReturnType = ConstructedReturnType.TranslateType(method.Parameters[i].ReturnType, typeArguments, true); + } + for (int i = 0; i < Math.Min(typeArguments.Count, method.TypeParameters.Count); i++) { + var tp = new BoundTypeParameter(method.TypeParameters[i], method.DeclaringType, method); + tp.BoundTo = typeArguments[i]; + method.TypeParameters[i] = tp; + } + return method; + } else { + return genericMethod; + } + } + + void CheckApplicability() + { + LogStep("Step 3 (CheckApplicability)"); + foreach (Candidate candidate in candidates) { + int c = Math.Min(arguments.Count, candidate.Parameters.Count); + for (int i = 0; i < c; i++) { + if (MemberLookupHelper.IsApplicable(arguments[i], candidate.Parameters[i], candidate.Method as IMethod)) + candidate.ApplicableArgumentCount++; + } + if (candidate.Status == CandidateStatus.Success && candidate.ApplicableArgumentCount < arguments.Count) { + candidate.Status = CandidateStatus.NotApplicable; + } + } + } + + Candidate FindBestCandidate() + { + LogStep("Step 4 (FindBestCandidate)"); + + // Find a candidate that is better than all other candidates + Candidate best = null; + foreach (Candidate candidate in candidates) { + if (candidate.Status == CandidateStatus.Success) { + if (best == null || GetBetterFunctionMember(best, candidate) == 2) { + best = candidate; + } + } + } + + if (best != null) + return best; + + // no successful candidate found: + // find the candidate that is nearest to being applicable + // first try only candidates with the correct parameter count + foreach (Candidate candidate in candidates) { + if (candidate.Status != CandidateStatus.WrongParameterCount) { + if (best == null || candidate.ApplicableArgumentCount > best.ApplicableArgumentCount) + best = candidate; + } + } + if (best != null) + return best; + // if all candidates have the wrong parameter count, return the candidate + // with the most applicable parameters. + best = candidates[0]; + foreach (Candidate candidate in candidates) { + if (candidate.ApplicableArgumentCount > best.ApplicableArgumentCount) + best = candidate; + } + return best; + } + + /// + /// Gets which function member is better. (§ 14.4.2.2) + /// + /// 0 if neither method is better. 1 if c1 is better. 2 if c2 is better. + int GetBetterFunctionMember(Candidate c1, Candidate c2) + { + int length = Math.Min(Math.Min(c1.Parameters.Count, c2.Parameters.Count), arguments.Count); + bool foundBetterParamIn1 = false; + bool foundBetterParamIn2 = false; + for (int i = 0; i < length; i++) { + if (arguments[i] == null) + continue; + int res = MemberLookupHelper.GetBetterConversion(arguments[i], c1.Parameters[i].ReturnType, c2.Parameters[i].ReturnType); + if (res == 1) foundBetterParamIn1 = true; + if (res == 2) foundBetterParamIn2 = true; + } + if (foundBetterParamIn1 && !foundBetterParamIn2) + return 1; + if (foundBetterParamIn2 && !foundBetterParamIn1) + return 2; + if (foundBetterParamIn1 && foundBetterParamIn2) + return 0; // ambigous + // If none conversion is better than any other, it is possible that the + // expanded parameter lists are the same: + for (int i = 0; i < length; i++) { + if (!object.Equals(c1.Parameters[i].ReturnType, c2.Parameters[i].ReturnType)) { + // if expanded parameters are not the same, neither function member is better + return 0; + } + } + + // the expanded parameters are the same, apply the tie-breaking rules from the spec: + + // if one method is generic and the other non-generic, the non-generic is better + bool m1IsGeneric = c1.TypeParameterCount > 0; + bool m2IsGeneric = c2.TypeParameterCount > 0; + if (m1IsGeneric && !m2IsGeneric) return 2; + if (m2IsGeneric && !m1IsGeneric) return 1; + + // for params parameters: non-expanded calls are better + if (c1.IsExpanded && !c2.IsExpanded) return 2; + if (c2.IsExpanded && !c1.IsExpanded) return 1; + + // if the number of parameters is different, the one with more parameters is better + // this occurs when only when both methods are expanded + if (c1.OriginalMethod.Parameters.Count > c2.OriginalMethod.Parameters.Count) return 1; + if (c2.OriginalMethod.Parameters.Count > c1.OriginalMethod.Parameters.Count) return 2; + + IReturnType[] m1ParamTypes = new IReturnType[c1.Parameters.Count]; + IReturnType[] m2ParamTypes = new IReturnType[c2.Parameters.Count]; + for (int i = 0; i < m1ParamTypes.Length; i++) { + m1ParamTypes[i] = c1.Parameters[i].ReturnType; + m2ParamTypes[i] = c2.Parameters[i].ReturnType; + } + return GetMoreSpecific(m1ParamTypes, m2ParamTypes); + } + + + /// + /// Gets which return type list is more specific. + /// § 14.4.2.2: types with generic arguments are less specific than types with fixed arguments + /// + /// 0 if both are equally specific, 1 if is more specific, + /// 2 if is more specific. + static int GetMoreSpecific(IList r, IList s) + { + bool foundMoreSpecificParamIn1 = false; + bool foundMoreSpecificParamIn2 = false; + int length = Math.Min(r.Count, s.Count); + for (int i = 0; i < length; i++) { + int res = GetMoreSpecific(r[i], s[i]); + if (res == 1) foundMoreSpecificParamIn1 = true; + if (res == 2) foundMoreSpecificParamIn2 = true; + } + if (foundMoreSpecificParamIn1 && !foundMoreSpecificParamIn2) + return 1; + if (foundMoreSpecificParamIn2 && !foundMoreSpecificParamIn1) + return 2; + return 0; + } + + static int GetMoreSpecific(IReturnType r, IReturnType s) + { + if (r == null && s == null) return 0; + if (r == null) return 2; + if (s == null) return 1; + if (r.IsGenericReturnType && !(s.IsGenericReturnType)) + return 2; + if (s.IsGenericReturnType && !(r.IsGenericReturnType)) + return 1; + if (r.IsArrayReturnType && s.IsArrayReturnType) + return GetMoreSpecific(r.CastToArrayReturnType().ArrayElementType, s.CastToArrayReturnType().ArrayElementType); + if (r.IsConstructedReturnType && s.IsConstructedReturnType) + return GetMoreSpecific(r.CastToConstructedReturnType().TypeArguments, s.CastToConstructedReturnType().TypeArguments); + return 0; + } + + [System.Diagnostics.ConditionalAttribute("DEBUG")] + void LogStep(string title) + { + MemberLookupHelper.Log(" candidates = ", candidates); + MemberLookupHelper.Log("Overload resolution (" + title + ")"); + } + + [System.Diagnostics.ConditionalAttribute("DEBUG")] + void Log(string text) + { + MemberLookupHelper.Log(text); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/TypeInference.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/TypeInference.cs new file mode 100644 index 000000000..2fe02e663 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/TypeInference.cs @@ -0,0 +1,476 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.SharpDevelop.Dom.CSharp +{ + /// + /// Implements C# 3.0 type inference. + /// + sealed class TypeInference + { + private TypeInference() {} + + public static IReturnType[] InferTypeArguments(IMethod method, IList arguments, out bool success) + { + TypeInference ti = new TypeInference(); + Log("Doing type inference for " + new CSharpAmbience().Convert(method)); + Log(" with arguments = ", arguments); + ti.typeParameters = method.TypeParameters.Select(tp => new TP(tp)).ToList(); + ti.parameterTypes = method.Parameters.Select(p => p.ReturnType).Take(arguments.Count).ToList(); + ti.arguments = arguments.Take(ti.parameterTypes.Count).ToArray(); + ti.PhaseOne(); + success = ti.PhaseTwo(); + IReturnType[] result = ti.typeParameters.Select(tp => tp.FixedTo).ToArray(); + Log("Type inference for " + method.DotNetName + " " + (success ? "succeeded" : "failed") + ": ", result); + return result; + } + + List typeParameters; + List parameterTypes; + IList arguments; + + sealed class TP { + public readonly ITypeParameter TypeParameter; + public IReturnType FixedTo; + public HashSet Bounds = new HashSet(); + + public bool Fixed { + get { return FixedTo != null; } + } + + public TP(ITypeParameter typeParameter) + { + this.TypeParameter = typeParameter; + } + + /// + /// Gets whether this type parameter occurs in the specified return type. + /// + public bool OccursIn(IReturnType rt) + { + ArrayReturnType art = rt.CastToArrayReturnType(); + if (art != null) { + return OccursIn(art.ArrayElementType); + } + ConstructedReturnType crt = rt.CastToConstructedReturnType(); + if (crt != null) { + return crt.TypeArguments.Any(ta => OccursIn(ta)); + } + GenericReturnType grt = rt.CastToGenericReturnType(); + if (grt != null) { + return this.TypeParameter.Equals(grt.TypeParameter); + } + return false; + } + + public override string ToString() + { + return TypeParameter.Method.Name + "." + TypeParameter.Name; + } + } + + void PhaseOne() + { + Log("Phase One"); + for (int i = 0; i < arguments.Count; i++) { + IReturnType ei = arguments[i]; + IReturnType Ti = parameterTypes[i]; + if (ei is AnonymousMethodReturnType || ei is MethodGroupReturnType) { + Log("MakeExplicitParameterTypeInference for #" + i); + MakeExplicitParameterTypeInference(ei, Ti); + + if (OutputTypeContainsUnfixed(ei, Ti) && !InputTypesContainsUnfixed(ei, Ti)) { + // an output type inference (§7.4.2.6) is made for ei with type Ti. + Log("MakeOutputTypeInference for #" + i); + MakeOutputTypeInference(ei, Ti); + } + } else { + Log("MakeOutputTypeInference for #" + i); + MakeOutputTypeInference(ei, Ti); + } + } + } + + bool PhaseTwo() + { + Log("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.Fixed == false) { + if (!typeParameters.Any((TP Xj) => 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) { + foreach (TP Xi in typeParameters) { + // Xi has a non­empty set of bounds + if (Xi.Fixed == false && Xi.Bounds.Count > 0) { + // 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.Fixed == false); + if (typeParametersToFix.Count == 0 && unfixedTypeVariablesExist) { + // If no such type variables exist and there are still unfixed type variables, type inference fails. + 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.Count; i++) { + IReturnType ei = arguments[i]; + IReturnType 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("MakeOutputTypeInference for #" + i); + MakeOutputTypeInference(ei, Ti); + } + } + // Then the second phase is repeated. + return PhaseTwo(); + } + } + + bool OutputTypeContainsUnfixed(IReturnType argumentType, IReturnType parameterType) + { + return OutputTypes(argumentType, parameterType).Any(t => TypeContainsUnfixedParameter(t)); + } + + bool InputTypesContainsUnfixed(IReturnType argumentType, IReturnType parameterType) + { + return InputTypes(argumentType, parameterType).Any(t => TypeContainsUnfixedParameter(t)); + } + + bool TypeContainsUnfixedParameter(IReturnType type) + { + return typeParameters.Where(tp => !tp.Fixed).Any(tp => tp.OccursIn(type)); + } + + IEnumerable OutputTypes(IReturnType e, IReturnType T) + { + AnonymousMethodReturnType amrt = e as AnonymousMethodReturnType; + if (amrt != null || e is MethodGroupReturnType) { + IMethod m = GetDelegateOrExpressionTreeSignature(T, amrt != null && amrt.CanBeConvertedToExpressionTree); + if (m != null) { + return new[] { m.ReturnType }; + } + } + return EmptyList.Instance; + } + + IEnumerable InputTypes(IReturnType e, IReturnType T) + { + AnonymousMethodReturnType amrt = e as AnonymousMethodReturnType; + if (amrt != null && amrt.HasImplicitlyTypedParameters || e is MethodGroupReturnType) { + IMethod m = GetDelegateOrExpressionTreeSignature(T, amrt != null && amrt.CanBeConvertedToExpressionTree); + if (m != null) { + return m.Parameters.Select(p => p.ReturnType); + } + } + return EmptyList.Instance; + } + + internal static IMethod GetDelegateOrExpressionTreeSignature(IReturnType rt, bool allowExpressionTree) + { + if (rt == null) + return null; + IClass c = rt.GetUnderlyingClass(); + if (allowExpressionTree && c != null && c.FullyQualifiedName == "System.Linq.Expressions.Expression") { + ConstructedReturnType crt = rt.CastToConstructedReturnType(); + if (crt != null && crt.TypeArguments.Count == 1) { + // get delegate type from expression type + rt = crt.TypeArguments[0]; + c = rt != null ? rt.GetUnderlyingClass() : null; + } + } + if (c != null && c.ClassType == ClassType.Delegate) { + return rt.GetMethods().FirstOrDefault((IMethod m) => m.Name == "Invoke"); + } + return null; + } + + bool DependsDirectlyOn(TP Xi, TP Xj) + { + if (Xj.Fixed) + return false; + for (int k = 0; k < arguments.Count; k++) { + if (InputTypes(arguments[k], parameterTypes[k]).Any(t => Xj.OccursIn(t)) + && OutputTypes(arguments[k], parameterTypes[k]).Any(t => Xi.OccursIn(t))) + { + return true; + } + } + return false; + } + + void AddDependencies(HashSet hash, TP Xi) + { + foreach (TP Xj in typeParameters) { + if (DependsDirectlyOn(Xi, Xj)) { + if (hash.Add(Xj)) + AddDependencies(hash, Xj); + } + } + } + + HashSet GetDependencies(TP X) + { + HashSet hash = new HashSet(); + AddDependencies(hash, X); + return hash; + } + + bool DependsOn(TP Xi, TP Xj) + { + return GetDependencies(Xi).Contains(Xj); + } + + void MakeOutputTypeInference(IReturnType e, IReturnType T) + { + //If e is an anonymous function with inferred return type  U (§7.4.2.11) and T is + // a delegate type or expression tree type with return type Tb, then a lower­bound + // inference (§7.4.2.9) is made from U for Tb. + AnonymousMethodReturnType amrt = e as AnonymousMethodReturnType; + if (amrt != null) { + IMethod m = GetDelegateOrExpressionTreeSignature(T, amrt.CanBeConvertedToExpressionTree); + if (m != null) { + IReturnType inferredReturnType; + if (amrt.HasParameterList && amrt.MethodParameters.Count == m.Parameters.Count) { + var inferredParameterTypes = m.Parameters.Select(p => SubstituteFixedTypes(p.ReturnType)).ToArray(); + inferredReturnType = amrt.ResolveReturnType(inferredParameterTypes); + } else { + inferredReturnType = amrt.ResolveReturnType(); + } + + MakeLowerBoundInference(inferredReturnType, m.ReturnType); + return; + } + } + // Otherwise, if e is a method group and T is a delegate type or expression tree type + // return type Tb 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 for Tb. + if (e is MethodGroupReturnType) { + // the MS C# doesn't seem to implement this rule, so we can safely skip this + return; + } + // Otherwise, if e is an expression with type U, then a lower­bound inference is made from + // U for T. + MakeLowerBoundInference(e, T); + } + + IReturnType SubstituteFixedTypes(IReturnType rt) + { + return ConstructedReturnType.TranslateType( + rt, typeParameters.Select(tp => tp.FixedTo).ToList(), true); + } + + void MakeExplicitParameterTypeInference(IReturnType e, IReturnType T) + { + // If e is an explicitly typed anonymous function with parameter types U1…Uk and T is a + // delegate type with parameter types V1…Vk then for each Ui an exact inference (§7.4.2.8) + // is made from Ui for the corresponding Vi. + AnonymousMethodReturnType amrt = e as AnonymousMethodReturnType; + if (amrt != null && amrt.HasParameterList) { + IMethod m = GetDelegateOrExpressionTreeSignature(T, amrt.CanBeConvertedToExpressionTree); + if (m != null && amrt.MethodParameters.Count == m.Parameters.Count) { + for (int i = 0; i < amrt.MethodParameters.Count; i++) { + MakeExactInference(amrt.MethodParameters[i].ReturnType, m.Parameters[i].ReturnType); + } + } + } + } + + /// + /// Make exact inference from U for V. + /// + void MakeExactInference(IReturnType U, IReturnType V) + { + Log(" MakeExactInference from " + U + " for " + V); + if (U == null || V == null) + return; + + // 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.Fixed == false) { + Log(" Add bound '" + U.DotNetName + "' to " + tp); + tp.Bounds.Add(U); + return; + } + // Otherwise if U is an array type Ue[…] and V is an array type Ve[…] of the same rank + // then an exact inference from Ue to Ve is made + ArrayReturnType arrU = U.CastToArrayReturnType(); + ArrayReturnType arrV = V.CastToArrayReturnType(); + if (arrU != null && arrV != null && arrU.ArrayDimensions == arrV.ArrayDimensions) { + MakeExactInference(arrU.ArrayElementType, arrV.ArrayElementType); + return; + } + // Otherwise if V is a constructed type C and U is a constructed + // type C then an exact inference is made from each Ui to the corresponding Vi. + ConstructedReturnType CU = U.CastToConstructedReturnType(); + ConstructedReturnType CV = V.CastToConstructedReturnType(); + if (CU != null && CV != null + && object.Equals(CU.UnboundType, CV.UnboundType) + && CU.TypeArgumentCount == CV.TypeArgumentCount) + { + for (int i = 0; i < CU.TypeArgumentCount; i++) { + MakeExactInference(CU.TypeArguments[i], CV.TypeArguments[i]); + } + return; + } + } + + TP GetTPForType(IReturnType t) + { + if (t == null) + return null; + GenericReturnType grt = t.CastToGenericReturnType(); + if (grt != null) { + return typeParameters.FirstOrDefault(tp => tp.TypeParameter.Equals(grt.TypeParameter)); + } + return null; + } + + /// + /// Make lower bound inference from U for V. + /// + void MakeLowerBoundInference(IReturnType U, IReturnType V) + { + Log(" MakeLowerBoundInference from " + U + " for " + V); + if (U == null || V == null) + return; + + // 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.Fixed == false) { + Log(" Add bound '" + U.DotNetName + "' to " + tp); + tp.Bounds.Add(U); + return; + } + // Otherwise if U is an array type Ue[…] and V is either an array type Ve[…]of the + // same rank, or if U is a one­dimensional array type Ue[]and V is one of + // IEnumerable, ICollection or IList then + ArrayReturnType arrU = U.CastToArrayReturnType(); + ArrayReturnType arrV = V.CastToArrayReturnType(); + ConstructedReturnType CV = V.CastToConstructedReturnType(); + if (arrU != null && + (arrV != null && arrU.ArrayDimensions == arrV.ArrayDimensions + || (arrU.ArrayDimensions == 1 && IsIEnumerableCollectionOrList(CV)))) + { + IReturnType Ue = arrU.ArrayElementType; + IReturnType Ve = arrV != null ? arrV.ArrayElementType : CV.TypeArguments[0]; + // If Ue is known to be a reference type then a lower­bound inference from Ue to Ve is made + if (IsReferenceType(Ue) ?? false) { + MakeLowerBoundInference(Ue, Ve); + } else { + // Otherwise an exact inference from Ue to Ve is made + MakeExactInference(Ue, Ve); + } + return; + } + // Otherwise if V is a constructed type C and there is a unique set of + // types U1…Uk such that a standard implicit conversion exists from U to C + // then an exact inference is made from each Ui for the corresponding Vi. + if (CV != null) { + foreach (IReturnType U2 in MemberLookupHelper.GetTypeInheritanceTree(U)) { + ConstructedReturnType CU2 = U2.CastToConstructedReturnType(); + if (CU2 != null && + object.Equals(CU2.UnboundType, CV.UnboundType) && + CU2.TypeArgumentCount == CV.TypeArgumentCount) + { + for (int i = 0; i < CU2.TypeArgumentCount; i++) { + MakeExactInference(CU2.TypeArguments[i], CV.TypeArguments[i]); + } + return; + } + } + } + } + + bool IsIEnumerableCollectionOrList(ConstructedReturnType rt) + { + if (rt == null || rt.TypeArgumentCount != 1) + return false; + switch (rt.UnboundType.FullyQualifiedName) { + case "System.Collections.Generic.IList": + case "System.Collections.Generic.ICollection": + case "System.Collections.Generic.IEnumerable": + return true; + default: + return false; + } + } + + bool? IsReferenceType(IReturnType rt) + { + if (rt == null) + return null; + IClass c = rt.GetUnderlyingClass(); + if (c == null) + return null; + switch (c.ClassType) { + case ClassType.Enum: + case ClassType.Struct: + return false; + default: + return true; + } + } + + bool Fix(TP X) + { + Log("Trying to fix " + X); + Log(" bounds = ", X.Bounds); + List candidates = new List(X.Bounds); + foreach (IReturnType U in X.Bounds) { + candidates.RemoveAll((IReturnType candidate) => !MemberLookupHelper.ConversionExists(U, candidate)); + } + Log(" candidates after removal round = ", candidates); + if (candidates.Count == 0) + return false; + var results = candidates.Where( + c1 => candidates.All(c2 => MemberLookupHelper.ConversionExists(c1, c2)) + ).ToList(); + Log(" possible solutions (should be exactly one) = ", candidates); + if (results.Count == 1) { + X.FixedTo = results[0]; + return true; + } else { + return false; + } + } + + [System.Diagnostics.ConditionalAttribute("DEBUG")] + static void Log(string text) + { + MemberLookupHelper.Log(text); + } + + [System.Diagnostics.ConditionalAttribute("DEBUG")] + static void Log(string text, IEnumerable types) + { + MemberLookupHelper.Log(text, types); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs new file mode 100644 index 000000000..9ab5b6b75 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs @@ -0,0 +1,564 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +using ICSharpCode.SharpDevelop.Dom.ReflectionLayer; +using Mono.Cecil; +using Mono.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public static class CecilReader + { + sealed class DummyAssemblyResolver : IAssemblyResolver + { + public AssemblyDefinition Resolve(AssemblyNameReference name) + { + return null; + } + + public AssemblyDefinition Resolve(string fullName) + { + return null; + } + + public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) + { + throw new NotImplementedException(); + } + + public AssemblyDefinition Resolve(string fullName, ReaderParameters parameters) + { + throw new NotImplementedException(); + } + } + + public static ReflectionProjectContent LoadAssembly(string fileName, ProjectContentRegistry registry) + { + if (fileName == null) + throw new ArgumentNullException("fileName"); + if (registry == null) + throw new ArgumentNullException("registry"); + LoggingService.Info("Cecil: Load from " + fileName); + AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(fileName, new ReaderParameters { AssemblyResolver = new DummyAssemblyResolver() }); + List referencedAssemblies = new List(); + foreach (ModuleDefinition module in asm.Modules) { + foreach (AssemblyNameReference anr in module.AssemblyReferences) { + referencedAssemblies.Add(new DomAssemblyName(anr.FullName)); + } + } + return new CecilProjectContent(asm.Name.FullName, fileName, referencedAssemblies.ToArray(), asm, registry); + } + + static void AddAttributes(IProjectContent pc, IEntity member, IList list, ICustomAttributeProvider attributeProvider) + { + if (!attributeProvider.HasCustomAttributes) + return; + foreach (CustomAttribute att in attributeProvider.CustomAttributes) { + DefaultAttribute a = new DefaultAttribute(CreateType(pc, member, att.Constructor.DeclaringType)); + // Currently Cecil returns string instead of TypeReference for typeof() arguments to attributes + try { + foreach (var argument in att.ConstructorArguments) { + a.PositionalArguments.Add(GetValue(pc, member, argument)); + } + foreach (CustomAttributeNamedArgument entry in att.Properties) { + // some obfuscated assemblies may contain duplicate named arguments; we'll have to ignore those + if (!a.NamedArguments.ContainsKey(entry.Name)) + a.NamedArguments.Add(entry.Name, GetValue(pc, member, entry.Argument)); + } + } catch (InvalidOperationException) { + // Workaround for Cecil bug. (some types cannot be resolved) + } + list.Add(a); + } + } + + static object GetValue(IProjectContent pc, IEntity member, CustomAttributeArgument argument) + { + if (argument.Value is TypeReference) + return CreateType(pc, member, (TypeReference)argument.Value); + else + return argument.Value; + } + + static void AddConstraintsFromType(ITypeParameter tp, GenericParameter g) + { + foreach (TypeReference constraint in g.Constraints) { + if (tp.Method != null) { + tp.Constraints.Add(CreateType(tp.Class.ProjectContent, tp.Method, constraint)); + } else { + tp.Constraints.Add(CreateType(tp.Class.ProjectContent, tp.Class, constraint)); + } + } + } + + /// + /// Create a SharpDevelop return type from a Cecil type reference. + /// + internal static IReturnType CreateType(IProjectContent pc, IEntity member, TypeReference type, ICustomAttributeProvider attributeProvider = null) + { + int typeIndex = 0; + return CreateType(pc, member, type, attributeProvider, ref typeIndex); + } + + static IReturnType CreateType(IProjectContent pc, IEntity member, TypeReference type, ICustomAttributeProvider attributeProvider, ref int typeIndex) + { + while (type is OptionalModifierType || type is RequiredModifierType) { + type = ((TypeSpecification)type).ElementType; + } + if (type == null) { + LoggingService.Warn("CecilReader: Null type for: " + member); + return new VoidReturnType(pc); + } + if (type is ByReferenceType) { + // TODO: Use ByRefRefReturnType + return CreateType(pc, member, (type as ByReferenceType).ElementType, attributeProvider, ref typeIndex); + } else if (type is PointerType) { + typeIndex++; + return new PointerReturnType(CreateType(pc, member, (type as PointerType).ElementType, attributeProvider, ref typeIndex)); + } else if (type is ArrayType) { + typeIndex++; + return new ArrayReturnType(pc, CreateType(pc, member, (type as ArrayType).ElementType, attributeProvider, ref typeIndex), (type as ArrayType).Rank); + } else if (type is GenericInstanceType) { + GenericInstanceType gType = (GenericInstanceType)type; + IReturnType baseType = CreateType(pc, member, gType.ElementType, attributeProvider, ref typeIndex); + IReturnType[] para = new IReturnType[gType.GenericArguments.Count]; + for (int i = 0; i < para.Length; ++i) { + typeIndex++; + para[i] = CreateType(pc, member, gType.GenericArguments[i], attributeProvider, ref typeIndex); + } + return new ConstructedReturnType(baseType, para); + } else if (type is GenericParameter) { + GenericParameter typeGP = type as GenericParameter; + if (typeGP.Owner is MethodDefinition) { + IMethod method = member as IMethod; + if (method != null) { + if (typeGP.Position < method.TypeParameters.Count) { + return new GenericReturnType(method.TypeParameters[typeGP.Position]); + } + } + return new GenericReturnType(new DefaultTypeParameter(method, typeGP.Name, typeGP.Position)); + } else { + IClass c = (member is IClass) ? (IClass)member : (member is IMember) ? ((IMember)member).DeclaringType : null; + if (c != null && typeGP.Position < c.TypeParameters.Count) { + if (c.TypeParameters[typeGP.Position].Name == type.Name) { + return new GenericReturnType(c.TypeParameters[typeGP.Position]); + } + } + return new GenericReturnType(new DefaultTypeParameter(c, typeGP.Name, typeGP.Position)); + } + } else { + string name = type.FullName; + if (name == null) + throw new ApplicationException("type.FullName returned null. Type: " + type.ToString()); + + int typeParameterCount; + if (name.IndexOf('/') > 0) { + typeParameterCount = 0; + StringBuilder newName = new StringBuilder(); + foreach (string namepart in name.Split('/')) { + if (newName.Length > 0) + newName.Append('.'); + int partTypeParameterCount; + newName.Append(ReflectionClass.SplitTypeParameterCountFromReflectionName(namepart, out partTypeParameterCount)); + typeParameterCount += partTypeParameterCount; + } + name = newName.ToString(); + } else { + name = ReflectionClass.SplitTypeParameterCountFromReflectionName(name, out typeParameterCount); + } + + if (typeParameterCount == 0 && name == "System.Object" && HasDynamicAttribute(attributeProvider, typeIndex)) + return new DynamicReturnType(pc); + + IClass c = pc.GetClass(name, typeParameterCount); + if (c != null) { + return c.DefaultReturnType; + } else { + // example where name is not found: pointers like System.Char* + // or when the class is in a assembly that is not referenced + return new GetClassReturnType(pc, name, typeParameterCount); + } + } + } + + static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex) + { + if (attributeProvider == null || attributeProvider.HasCustomAttributes == false) + return false; + foreach (CustomAttribute a in attributeProvider.CustomAttributes) { + if (a.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.DynamicAttribute") { + if (a.ConstructorArguments.Count == 1) { + CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[]; + if (values != null && typeIndex < values.Length && values[typeIndex].Value is bool) + return (bool)values[typeIndex].Value; + } + return true; + } + } + return false; + } + + private sealed class CecilProjectContent : ReflectionProjectContent + { + public CecilProjectContent(string fullName, string fileName, DomAssemblyName[] referencedAssemblies, + AssemblyDefinition assembly, ProjectContentRegistry registry) + : base(fullName, fileName, referencedAssemblies, registry) + { + foreach (ModuleDefinition module in assembly.Modules) { + AddTypes(module.Types); + } + AddAttributes(this, null, this.AssemblyCompilationUnit.Attributes, assembly); + InitializeSpecialClasses(); + this.AssemblyCompilationUnit.Freeze(); + } + + void AddTypes(Collection types) + { + foreach (TypeDefinition td in types) { + if ((td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public) { + string name = td.FullName; + if (name.Length == 0 || name[0] == '<') + continue; + name = ReflectionClass.SplitTypeParameterCountFromReflectionName(name); + AddClassToNamespaceListInternal(new CecilClass(this.AssemblyCompilationUnit, null, td, name)); + } + } + } + } + + private sealed class CecilClass : DefaultClass + { + public static bool IsDelegate(TypeDefinition type) + { + if (type.BaseType == null) + return false; + else + return type.BaseType.FullName == "System.Delegate" + || type.BaseType.FullName == "System.MulticastDelegate"; + } + + protected override bool KeepInheritanceTree { + get { return true; } + } + + public CecilClass(ICompilationUnit compilationUnit, IClass declaringType, + TypeDefinition td, string fullName) + : base(compilationUnit, declaringType) + { + this.FullyQualifiedName = fullName; + + AddAttributes(compilationUnit.ProjectContent, this, this.Attributes, td); + + // set classtype + if (td.IsInterface) { + this.ClassType = ClassType.Interface; + } else if (td.IsEnum) { + this.ClassType = ClassType.Enum; + } else if (td.IsValueType) { + this.ClassType = ClassType.Struct; + } else if (IsDelegate(td)) { + this.ClassType = ClassType.Delegate; + } else { + this.ClassType = ClassType.Class; + } + if (td.GenericParameters.Count > 0) { + foreach (GenericParameter g in td.GenericParameters) { + this.TypeParameters.Add(new DefaultTypeParameter(this, g.Name, g.Position)); + } + int i = 0; + foreach (GenericParameter g in td.GenericParameters) { + AddConstraintsFromType(this.TypeParameters[i++], g); + } + } + + ModifierEnum modifiers = ModifierEnum.None; + + if (td.IsSealed) { + modifiers |= ModifierEnum.Sealed; + } + if (td.IsAbstract) { + modifiers |= ModifierEnum.Abstract; + } + if (td.IsSealed && td.IsAbstract) { + modifiers |= ModifierEnum.Static; + } + + if ((td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic) { + modifiers |= ModifierEnum.Public; + } else if ((td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily) { + modifiers |= ModifierEnum.Protected; + } else if ((td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem) { + modifiers |= ModifierEnum.Protected; + } else { + modifiers |= ModifierEnum.Public; + } + + this.Modifiers = modifiers; + + // set base classes + if (td.BaseType != null) { + BaseTypes.Add(CreateType(this.ProjectContent, this, td.BaseType)); + } + + foreach (TypeReference iface in td.Interfaces) { + BaseTypes.Add(CreateType(this.ProjectContent, this, iface)); + } + + ReflectionClass.ApplySpecialsFromAttributes(this); + + InitMembers(td); + } + + void InitMembers(TypeDefinition type) + { + string defaultMemberName = null; + foreach (CustomAttribute att in type.CustomAttributes) { + if (att.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute" + && att.ConstructorArguments.Count == 1) + { + defaultMemberName = att.ConstructorArguments[0].Value as string; + } + } + + foreach (TypeDefinition nestedType in type.NestedTypes) { + TypeAttributes visibility = nestedType.Attributes & TypeAttributes.VisibilityMask; + if (visibility == TypeAttributes.NestedPublic || visibility == TypeAttributes.NestedFamily + || visibility == TypeAttributes.NestedFamORAssem) + { + string name = nestedType.Name; + int pos = name.LastIndexOf('/'); + if (pos > 0) + name = name.Substring(pos + 1); + if (name.Length == 0 || name[0] == '<') + continue; + name = ReflectionClass.SplitTypeParameterCountFromReflectionName(name); + name = this.FullyQualifiedName + "." + name; + InnerClasses.Add(new CecilClass(this.CompilationUnit, this, nestedType, name)); + } + } + + foreach (FieldDefinition field in type.Fields) { + if (IsVisible(field.Attributes) && !field.IsSpecialName) { + DefaultField f = new DefaultField(this, field.Name); + f.Modifiers = TranslateModifiers(field); + f.ReturnType = CreateType(this.ProjectContent, this, field.FieldType, field); + AddAttributes(CompilationUnit.ProjectContent, f, f.Attributes, field); + Fields.Add(f); + } + } + + foreach (PropertyDefinition property in type.Properties) { + AddProperty(defaultMemberName, property); + } + + foreach (EventDefinition eventDef in type.Events) { + if (eventDef.AddMethod != null && IsVisible(eventDef.AddMethod.Attributes)) { + DefaultEvent e = new DefaultEvent(this, eventDef.Name); + if (this.ClassType == ClassType.Interface) { + e.Modifiers = ModifierEnum.Public | ModifierEnum.Abstract; + } else { + e.Modifiers = TranslateModifiers(eventDef); + } + e.ReturnType = CreateType(this.ProjectContent, this, eventDef.EventType, eventDef); + AddAttributes(CompilationUnit.ProjectContent, e, e.Attributes, eventDef); + Events.Add(e); + } + } + + this.AddDefaultConstructorIfRequired = (this.ClassType == ClassType.Struct || this.ClassType == ClassType.Enum); + foreach (MethodDefinition method in type.Methods) { + if (method.IsConstructor || !method.IsSpecialName) { + AddMethod(method); + } + } + } + + void AddProperty(string defaultMemberName, PropertyDefinition property) + { + if ((property.GetMethod != null && IsVisible(property.GetMethod.Attributes)) + || (property.SetMethod != null && IsVisible(property.SetMethod.Attributes))) + { + DefaultProperty p = new DefaultProperty(this, property.Name); + if (this.ClassType == ClassType.Interface) { + p.Modifiers = ModifierEnum.Public | ModifierEnum.Abstract; + } else { + p.Modifiers = TranslateModifiers(property); + } + p.ReturnType = CreateType(this.ProjectContent, this, property.PropertyType, property); + p.CanGet = property.GetMethod != null && IsVisible(property.GetMethod.Attributes); + p.CanSet = property.SetMethod != null && IsVisible(property.SetMethod.Attributes); + if (p.CanGet) + p.GetterModifiers = GetAccessorVisibility(p, property.GetMethod); + if (p.CanSet) + p.SetterModifiers = GetAccessorVisibility(p, property.SetMethod); + if (p.Name == defaultMemberName) { + p.IsIndexer = true; + } + AddParameters(p, property.Parameters); + AddAttributes(CompilationUnit.ProjectContent, p, p.Attributes, property); + Properties.Add(p); + } + } + + static ModifierEnum GetAccessorVisibility(IProperty p, MethodDefinition accessor) + { + ModifierEnum visibility = ModifierEnum.VisibilityMask & TranslateModifiers(accessor); + if (visibility == (p.Modifiers & ModifierEnum.VisibilityMask)) + return ModifierEnum.None; + else + return visibility; + } + + void AddMethod(MethodDefinition method) + { + if (IsVisible(method.Attributes)) { + DefaultMethod m = new DefaultMethod(this, method.IsConstructor ? "#ctor" : method.Name); + + if (method.GenericParameters.Count > 0) { + foreach (GenericParameter g in method.GenericParameters) { + m.TypeParameters.Add(new DefaultTypeParameter(m, g.Name, g.Position)); + } + int i = 0; + foreach (GenericParameter g in method.GenericParameters) { + AddConstraintsFromType(m.TypeParameters[i++], g); + } + } + + if (method.IsConstructor) + m.ReturnType = this.DefaultReturnType; + else + m.ReturnType = CreateType(this.ProjectContent, m, method.ReturnType, method.MethodReturnType); + AddAttributes(CompilationUnit.ProjectContent, m, m.Attributes, method); + if (this.ClassType == ClassType.Interface) { + m.Modifiers = ModifierEnum.Public | ModifierEnum.Abstract; + } else { + m.Modifiers = TranslateModifiers(method); + } + AddParameters(m, method.Parameters); + AddExplicitInterfaceImplementations(method.Overrides, m); + ReflectionLayer.ReflectionMethod.ApplySpecialsFromAttributes(m); + Methods.Add(m); + } + } + + void AddExplicitInterfaceImplementations(Collection overrides, IMember targetMember) + { + foreach (MethodReference overrideRef in overrides) { + if (overrideRef.Name == targetMember.Name && targetMember.IsPublic) { + continue; // is like implicit interface implementation / normal override + } + targetMember.InterfaceImplementations.Add(new ExplicitInterfaceImplementation( + CreateType(this.ProjectContent, targetMember, overrideRef.DeclaringType), + overrideRef.Name + )); + } + } + + void AddParameters(IMethodOrProperty target, Collection plist) + { + foreach (ParameterDefinition par in plist) { + IReturnType pReturnType = CreateType(this.ProjectContent, target, par.ParameterType, par); + DefaultParameter p = new DefaultParameter(par.Name, pReturnType, DomRegion.Empty); + if (par.ParameterType is ByReferenceType) { + if ((par.Attributes & ParameterAttributes.Out) == ParameterAttributes.Out) { + p.Modifiers = ParameterModifiers.Out; + } else { + p.Modifiers = ParameterModifiers.Ref; + } + } else { + p.Modifiers = ParameterModifiers.In; + } + if ((par.Attributes & ParameterAttributes.Optional) == ParameterAttributes.Optional) { + p.Modifiers |= ParameterModifiers.Optional; + } + if (p.ReturnType.IsArrayReturnType) { + foreach (CustomAttribute att in par.CustomAttributes) { + if (att.Constructor.DeclaringType.FullName == typeof(ParamArrayAttribute).FullName) { + p.Modifiers |= ParameterModifiers.Params; + } + } + } + target.Parameters.Add(p); + } + } + + static bool IsVisible(MethodAttributes att) + { + return ((att & MethodAttributes.Public) == MethodAttributes.Public) + || ((att & MethodAttributes.Family) == MethodAttributes.Family) + || ((att & MethodAttributes.FamORAssem) == MethodAttributes.FamORAssem); + } + + static bool IsVisible(FieldAttributes att) + { + return ((att & FieldAttributes.Public) == FieldAttributes.Public) + || ((att & FieldAttributes.Family) == FieldAttributes.Family) + || ((att & FieldAttributes.FamORAssem) == FieldAttributes.FamORAssem); + } + + static ModifierEnum TranslateModifiers(MethodDefinition method) + { + ModifierEnum m = ModifierEnum.None; + + if (method.IsStatic) { + m |= ModifierEnum.Static; + } else { + if (method.IsAbstract) { + m |= ModifierEnum.Abstract; + } else if (method.IsFinal) { + m |= ModifierEnum.Sealed; + } else if (method.Overrides.Count > 0) { + m |= ModifierEnum.Override; + } else if (method.IsVirtual) { + if (method.IsNewSlot) + m |= ModifierEnum.Virtual; + else + m |= ModifierEnum.Override; + } + } + + if ((method.Attributes & MethodAttributes.Public) == MethodAttributes.Public) + m |= ModifierEnum.Public; + else + m |= ModifierEnum.Protected; + + return m; + } + + static ModifierEnum TranslateModifiers(PropertyDefinition property) + { + return TranslateModifiers(property.GetMethod ?? property.SetMethod); + } + + static ModifierEnum TranslateModifiers(EventDefinition @event) + { + return TranslateModifiers(@event.AddMethod); + } + + static ModifierEnum TranslateModifiers(FieldDefinition field) + { + ModifierEnum m = ModifierEnum.None; + + if (field.IsStatic) + m |= ModifierEnum.Static; + + if (field.IsLiteral) + m |= ModifierEnum.Const; + else if (field.IsInitOnly) + m |= ModifierEnum.Readonly; + + if ((field.Attributes & FieldAttributes.Public) == FieldAttributes.Public) + m |= ModifierEnum.Public; + else + m |= ModifierEnum.Protected; + + return m; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ClassFinder.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ClassFinder.cs new file mode 100644 index 000000000..c83e342e3 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ClassFinder.cs @@ -0,0 +1,130 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Class that stores a source code context and can resolve type names + /// in that context. + /// + public sealed class ClassFinder + { + int caretLine, caretColumn; + ICompilationUnit cu; + IClass callingClass; + IProjectContent projectContent; + + public IClass CallingClass { + get { + return callingClass; + } + } + + public IProjectContent ProjectContent { + get { + return projectContent; + } + } + + public LanguageProperties Language { + get { + return projectContent.Language; + } + } + + public int CaretLine { + get { return caretLine; } + } + + public int CaretColumn { + get { return caretColumn; } + } + + public ClassFinder(ParseInformation parseInfo, string fileContent, int offset) + { + caretLine = 0; + caretColumn = 0; + for (int i = 0; i < offset; i++) { + if (fileContent[i] == '\n') { + caretLine++; + caretColumn = 0; + } else { + caretColumn++; + } + } + Init(parseInfo); + } + + public ClassFinder(ParseInformation parseInfo, int caretLineNumber, int caretColumn) + { + this.caretLine = caretLineNumber; + this.caretColumn = caretColumn; + + Init(parseInfo); + } + + public ClassFinder(IMember classMember) + : this(classMember.DeclaringType, classMember.Region.BeginLine, classMember.Region.BeginColumn) + { + } + + public ClassFinder(IClass callingClass, int caretLine, int caretColumn) + { + if (callingClass == null) + throw new ArgumentNullException("callingClass"); + if (callingClass is CompoundClass) + throw new ArgumentException("Cannot use compound class for ClassFinder - must pass a specific class part."); + this.caretLine = caretLine; + this.caretColumn = caretColumn; + this.callingClass = callingClass; + this.cu = callingClass.CompilationUnit; + this.projectContent = cu.ProjectContent; + if (projectContent == null) + throw new ArgumentException("callingClass must have a project content!"); + } + + // currently callingMember is not required + public ClassFinder(IClass callingClass, IMember callingMember, int caretLine, int caretColumn) + : this(callingClass, caretLine, caretColumn) + { + } + + void Init(ParseInformation parseInfo) + { + if (parseInfo != null) { + cu = parseInfo.CompilationUnit; + } + + if (cu != null) { + callingClass = cu.GetInnermostClass(caretLine, caretColumn); + projectContent = cu.ProjectContent; + } else { + projectContent = DefaultProjectContent.DummyProjectContent; + } + if (projectContent == null) + throw new ArgumentException("projectContent not found!"); + } + + public IClass GetClass(string fullName, int typeParameterCount) + { + return projectContent.GetClass(fullName, typeParameterCount); + } + + public IReturnType SearchType(string name, int typeParameterCount) + { + return Search(name, typeParameterCount).Result; + } + + public SearchTypeResult Search(string name, int typeParameterCount) + { + return projectContent.SearchType(new SearchTypeRequest(name, typeParameterCount, callingClass, cu, caretLine, caretColumn)); + } + + public string SearchNamespace(string name) + { + return Search(name, 0).NamespaceResult; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CtrlSpaceResolveHelper.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CtrlSpaceResolveHelper.cs new file mode 100644 index 000000000..11671b10f --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/CtrlSpaceResolveHelper.cs @@ -0,0 +1,285 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Provides static methods that fill a list of completion results with entries + /// reachable from a certain calling class/member or entries that introduced + /// by a certain Using statement. + /// + public class CtrlSpaceResolveHelper + { + static void AddTypeParametersForCtrlSpace(List result, IEnumerable typeParameters) + { + foreach (ITypeParameter p in typeParameters) { + DefaultClass c = DefaultTypeParameter.GetDummyClassForTypeParameter(p); + if (p.Method != null) { + c.Documentation = "Type parameter of " + p.Method.Name; + } else { + c.Documentation = "Type parameter of " + p.Class.Name; + } + result.Add(c); + } + } + + public static void AddContentsFromCalling(List result, IClass callingClass, IMember callingMember) + { + IMethodOrProperty methodOrProperty = callingMember as IMethodOrProperty; + if (methodOrProperty != null) { + foreach (IParameter p in methodOrProperty.Parameters) { + result.Add(new DefaultField.ParameterField(p.ReturnType, p.Name, methodOrProperty.Region, callingClass)); + } + if (callingMember is IMethod) { + AddTypeParametersForCtrlSpace(result, ((IMethod)callingMember).TypeParameters); + } + } + + bool inStatic = false; + if (callingMember != null) + inStatic = callingMember.IsStatic; + + if (callingClass != null) { + AddTypeParametersForCtrlSpace(result, callingClass.TypeParameters); + + + List members = new List(); + IReturnType t = callingClass.DefaultReturnType; + var language = callingClass.ProjectContent.Language; + foreach (IMember m in MemberLookupHelper.GetAccessibleMembers(t, callingClass, language, true)) { + if ((!inStatic || m.IsStatic) && language.ShowMember(m, m.IsStatic)) + result.Add(m); + } + members.Clear(); + IClass c = callingClass.DeclaringType; + while (c != null) { + t = c.DefaultReturnType; + foreach (IMember m in MemberLookupHelper.GetAccessibleMembers(t, c, language, true)) { + if (language.ShowMember(m, true)) + result.Add(m); + } + c = c.DeclaringType; + } + } + } + + /// + /// Adds contents of all assemblies referenced by 's project. + /// Also adds contents of . + /// + public static void AddReferencedProjectsContents(List result, ICompilationUnit cu, IClass callingClass) + { + IProjectContent projectContent = cu.ProjectContent; + projectContent.AddNamespaceContents(result, "", projectContent.Language, true); + var allContents = projectContent.GetAllContents(); + result.Capacity = result.Count + allContents.Count; + foreach (var entry in allContents.Where(e => !(e is NamespaceEntry))) { + result.Add(entry); + } + AddUsing(result, projectContent.DefaultImports, projectContent); + AddContentsFromCallingClass(result, projectContent, callingClass); + } + + /// + /// Adds contents of all namespaces that this imports to the list. + /// Also adds contents of . + /// + public static void AddImportedNamespaceContents(List result, ICompilationUnit cu, IClass callingClass) + { + IProjectContent projectContent = cu.ProjectContent; + projectContent.AddNamespaceContents(result, "", projectContent.Language, true); + IUsingScope scope = (callingClass != null) ? callingClass.UsingScope : cu.UsingScope; + while (scope != null) { + foreach (IUsing u in scope.Usings) + AddUsing(result, u, projectContent); + scope = scope.Parent; + } + AddUsing(result, projectContent.DefaultImports, projectContent); + AddContentsFromCallingClass(result, projectContent, callingClass); + } + + static void AddContentsFromCallingClass(List result, IProjectContent projectContent, IClass callingClass) + { + if (callingClass == null) { + return; + } + // use HashSet so that Contains lookups are possible in O(1). + HashSet existingResults = new HashSet(result); + string[] namespaceParts = callingClass.Namespace.Split('.'); + for (int i = 1; i <= namespaceParts.Length; i++) { + foreach (ICompletionEntry member in projectContent.GetNamespaceContents(string.Join(".", namespaceParts, 0, i))) { + if (!existingResults.Contains(member)) + result.Add(member); + } + } + IClass currentClass = callingClass; + do { + foreach (IClass innerClass in currentClass.GetCompoundClass().GetAccessibleTypes(currentClass)) { + if (!existingResults.Contains(innerClass)) + result.Add(innerClass); + } + currentClass = currentClass.DeclaringType; + } while (currentClass != null); + } + + public static void AddUsing(List result, IUsing u, IProjectContent projectContent) + { + if (u == null) { + return; + } + bool importNamespaces = projectContent.Language.ImportNamespaces; + bool importClasses = projectContent.Language.CanImportClasses; + foreach (string name in u.Usings) { + if (importClasses) { + IClass c = projectContent.GetClass(name, 0); + if (c != null) { + ArrayList members = new ArrayList(); + IReturnType t = c.DefaultReturnType; + members.AddRange(t.GetMethods()); + members.AddRange(t.GetFields()); + members.AddRange(t.GetEvents()); + members.AddRange(t.GetProperties()); + foreach (IMember m in members) { + if (m.IsStatic && m.IsPublic) { + result.Add(m); + } + } + continue; + } + } + if (importNamespaces) { + string newName = null; + if (projectContent.DefaultImports != null) { + newName = projectContent.DefaultImports.SearchNamespace(name); + } + projectContent.AddNamespaceContents(result, newName ?? name, projectContent.Language, true); + } else { + foreach (ICompletionEntry o in projectContent.GetNamespaceContents(name)) { + if (!(o is NamespaceEntry)) + result.Add(o); + } + } + } + if (u.HasAliases) { + foreach (string alias in u.Aliases.Keys) { + result.Add(new AliasEntry(alias)); + } + } + } + + public class AliasEntry : ICompletionEntry + { + public string Name { get; private set; } + + public AliasEntry(string name) + { + if (name == null) + throw new ArgumentNullException("name"); + this.Name = name; + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override bool Equals(object obj) + { + AliasEntry e = obj as AliasEntry; + return e != null && e.Name == this.Name; + } + + public override string ToString() + { + return Name; + } + } + + public static ResolveResult GetResultFromDeclarationLine(IClass callingClass, IMethodOrProperty callingMember, int caretLine, int caretColumn, ExpressionResult expressionResult) + { + string expression = expressionResult.Expression; + if (expression == null) return null; + if (callingClass == null) return null; + int pos = expression.IndexOf('('); + if (pos >= 0) { + expression = expression.Substring(0, pos); + } + expression = expression.Trim(); +// if (!callingClass.BodyRegion.IsInside(caretLine, caretColumn) +// && callingClass.ProjectContent.Language.NameComparer.Equals(expression, callingClass.Name)) +// { +// return new TypeResolveResult(callingClass, callingMember, callingClass); +// } + if (expressionResult.Context != ExpressionContext.Type) { + if (callingMember != null + && !callingMember.BodyRegion.IsInside(caretLine, caretColumn) + && (callingClass.ProjectContent.Language.NameComparer.Equals(expression, callingMember.Name) || + // For constructor definition, the expression is the constructor name (e.g. "MyClass") but the name of the member is "#ctor" + (callingMember.Name == "#ctor" && callingClass.ProjectContent.Language.NameComparer.Equals(expression, callingClass.Name)) + ) + ) + { + return new MemberResolveResult(callingClass, callingMember, callingMember); + } + } + return null; + } + + public static IList FindAllExtensions(LanguageProperties language, IClass callingClass, bool searchInAllNamespaces = false) + { + if (language == null) + throw new ArgumentNullException("language"); + if (callingClass == null) + throw new ArgumentNullException("callingClass"); + + HashSet res = new HashSet(); + + bool supportsExtensionMethods = language.SupportsExtensionMethods; + bool supportsExtensionProperties = language.SupportsExtensionProperties; + if (supportsExtensionMethods || supportsExtensionProperties) { + List list = new List(); + IMethod dummyMethod = new DefaultMethod("dummy", callingClass.ProjectContent.SystemTypes.Void, + ModifierEnum.Static, DomRegion.Empty, DomRegion.Empty, callingClass); + CtrlSpaceResolveHelper.AddContentsFromCalling(list, callingClass, dummyMethod); + if (searchInAllNamespaces) { + // search extension methods in all referenced projects, no matter the using section + CtrlSpaceResolveHelper.AddReferencedProjectsContents(list, callingClass.CompilationUnit, callingClass); + } else { + CtrlSpaceResolveHelper.AddImportedNamespaceContents(list, callingClass.CompilationUnit, callingClass); + } + + bool searchExtensionsInClasses = language.SearchExtensionsInClasses; + foreach (object o in list) { + IMethodOrProperty mp = o as IMethodOrProperty; + if (mp != null && mp.IsExtensionMethod && + (supportsExtensionMethods && o is IMethod || supportsExtensionProperties && o is IProperty)) + { + res.Add(mp); + } else if (searchExtensionsInClasses && o is IClass) { + IClass c = o as IClass; + if (c.HasExtensionMethods) { + if (supportsExtensionProperties) { + foreach (IProperty p in c.Properties) { + if (p.IsExtensionMethod) + res.Add(p); + } + } + if (supportsExtensionMethods) { + foreach (IMethod m in c.Methods) { + if (m.IsExtensionMethod) + res.Add(m); + } + } + } + } + } + } + return res.ToList(); + } // FindAllExtensions + } // CtrlSpaceResolveHelper class +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/DiffUtility.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/DiffUtility.cs new file mode 100644 index 000000000..0f412318b --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/DiffUtility.cs @@ -0,0 +1,129 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public static class DiffUtility + { + public static int GetAddedItems(IList original, IList changed, IList result) + { + return GetAddedItems(original, changed, result, Comparer.Default); + } + + public static int GetAddedItems(IList original, IList changed, IList result, IComparer comparer) + { + int count = 0; + if(changed != null && result != null) { + if(original == null) { + foreach(object item in changed) { + result.Add(item); + } + count = changed.Count; + } + else { + foreach(object item in changed) { + if(!Contains(original, item, comparer)) { + result.Add(item); + count++; + } + } + } + } + return count; + } + + public static int GetRemovedItems(IList original, IList changed, IList result) + { + return GetRemovedItems(original, changed, result, Comparer.Default); + } + + public static int GetRemovedItems(IList original, IList changed, IList result, IComparer comparer) + { + return GetAddedItems(changed, original, result, comparer); + } + + static bool Contains(IList list, object value, IComparer comparer) + { + foreach(object item in list) { + if(0 == comparer.Compare(item, value)) { + return true; + } + } + return false; + } + + static public int Compare(IList a, IList b) + { + return Compare(a, b, Comparer.Default); + } + + static public int Compare(IList a, IList b) + { + return Compare(a, b, Comparer.Default); + } + + static public int Compare(IList a, IList b, IComparer comparer) + { + if (a == null || b == null) { + return 1; + } + if (a.Count != b.Count) { + return Math.Sign(a.Count - b.Count); + } + int limit = (a.Count < b.Count) ? a.Count : b.Count; + for(int i=0; i < limit; i++) { + if (a[i] is IComparable && b[i] is IComparable) { + int cmp = comparer.Compare(a[i], b[i]); + if (cmp != 0) { + return cmp; + } + } + } + return a.Count - b.Count; + } + + static public int Compare(IList a, IList b, IComparer comparer) + { + if (a == null || b == null) { + return 1; + } + if (a.Count != b.Count) { + return Math.Sign(a.Count - b.Count); + } + int limit = (a.Count < b.Count) ? a.Count : b.Count; + for(int i=0; i < limit; i++) { + if (a[i] is IComparable && b[i] is IComparable) { + int cmp = comparer.Compare(a[i], b[i]); + if (cmp != 0) { + return cmp; + } + } + } + return a.Count - b.Count; + } + + static public int Compare(SortedList a, SortedList b) + { + return Compare(a, b, Comparer.Default); + } + + static public int Compare(SortedList a, SortedList b, IComparer comparer) + { + if (a == null || b == null) { + return 1; + } + int cmp; + int limit = (a.Count < b.Count) ? a.Count : b.Count; + for(int i=0; i < limit; i++) { + if(0 != (cmp = comparer.Compare(a.GetByIndex(i), b.GetByIndex(i)))) { + return cmp; + } + } + return a.Count - b.Count; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/DomCache.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/DomCache.cs new file mode 100644 index 000000000..1d7934bc9 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/DomCache.cs @@ -0,0 +1,41 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + static class DomCache + { + /// + /// Clear the static searchclass cache. You should call this method + /// whenever the DOM changes. + /// + /// + /// automatically called by DefaultProjectContent.UpdateCompilationUnit + /// and DefaultProjectContent.OnReferencedContentsChanged. + /// + public static void Clear() + { + List oldActions; + lock (lockObject) { + oldActions = actions; + actions = new List(); + } + foreach (Action a in oldActions) { + a(); + } + } + + static readonly object lockObject = new Object(); + static List actions = new List(); + + public static void RegisterForClear(Action action) + { + lock (lockObject) { + actions.Add(action); + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/EasyCodeDom.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/EasyCodeDom.cs new file mode 100644 index 000000000..fcdaedbd0 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/EasyCodeDom.cs @@ -0,0 +1,382 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.CodeDom; + +namespace ICSharpCode.EasyCodeDom +{ + public static class Easy + { + public static CodeTypeReference TypeRef(Type type) + { + return new CodeTypeReference(type, CodeTypeReferenceOptions.GlobalReference); + } + public static CodeTypeReference TypeRef(CodeTypeDeclaration type) + { + return new CodeTypeReference(type.Name); + } + public static CodeTypeReference TypeRef(string typeName, params string[] typeArguments) + { + CodeTypeReference tr = new CodeTypeReference(typeName); + foreach (string ta in typeArguments) { + tr.TypeArguments.Add(ta); + } + return tr; + } + + /// + /// Gets the CodeExpression for any primitive value that can be expressed as literal. + /// Also works for enumeration values. + /// + public static CodeExpression Prim(object literalValue) + { + if (literalValue is Enum) { + return Type(literalValue.GetType()).Field(literalValue.ToString()); + } else { + return new CodePrimitiveExpression(literalValue); + } + } + + public static CodeTypeReferenceExpression Type(Type type) + { + return Type(TypeRef(type)); + } + public static CodeTypeReferenceExpression Type(CodeTypeReference type) + { + return new CodeTypeReferenceExpression(type); + } + public static CodeTypeReferenceExpression Type(string type) + { + return Type(new CodeTypeReference(type)); + } + + public static CodeTypeOfExpression TypeOf(Type type) + { + return TypeOf(TypeRef(type)); + } + public static CodeTypeOfExpression TypeOf(CodeTypeReference type) + { + return new CodeTypeOfExpression(type); + } + + public static CodeObjectCreateExpression New(Type type, params CodeExpression[] arguments) + { + return New(TypeRef(type), arguments); + } + public static CodeObjectCreateExpression New(CodeTypeReference type, params CodeExpression[] arguments) + { + return new CodeObjectCreateExpression(type, arguments); + } + + public static CodeVariableReferenceExpression Var(string name) + { + return new CodeVariableReferenceExpression(name); + } + + public static CodeBinaryOperatorExpression Binary(CodeExpression left, + CodeBinaryOperatorType op, + CodeExpression right) + { + return new CodeBinaryOperatorExpression(left, op, right); + } + + public static CodeThisReferenceExpression This { + get { + return new CodeThisReferenceExpression(); + } + } + + public static CodeBaseReferenceExpression Base { + get { + return new CodeBaseReferenceExpression(); + } + } + + public static CodePropertySetValueReferenceExpression Value { + get { + return new CodePropertySetValueReferenceExpression(); + } + } + + public static CodePrimitiveExpression Null { + get { + return new CodePrimitiveExpression(null); + } + } + + public static void AddSummary(CodeTypeMember member, string summary) + { + member.Comments.Add(new CodeCommentStatement("", true)); + member.Comments.Add(new CodeCommentStatement(summary, true)); + member.Comments.Add(new CodeCommentStatement("", true)); + } + + internal static CodeAttributeDeclaration AddAttribute(CodeAttributeDeclarationCollection col, + CodeTypeReference type, + CodeExpression[] arguments) + { + CodeAttributeArgument[] attributeArguments = new CodeAttributeArgument[arguments.Length]; + for (int i = 0; i < arguments.Length; i++) { + attributeArguments[i] = new CodeAttributeArgument(arguments[i]); + } + CodeAttributeDeclaration cad = new CodeAttributeDeclaration(type, attributeArguments); + col.Add(cad); + return cad; + } + } + + public static class ExtensionMethods + { + public static CodeMethodInvokeExpression InvokeMethod(this CodeExpression expr, string name, params CodeExpression[] arguments) + { + return new CodeMethodInvokeExpression(expr, name, arguments); + } + + public static CodeCastExpression CastTo(this CodeExpression expr, Type type) + { + return expr.CastTo(Easy.TypeRef(type)); + } + public static CodeCastExpression CastTo(this CodeExpression expr, CodeTypeReference type) + { + return new CodeCastExpression(type, expr); + } + + public static CodeIndexerExpression Index(this CodeExpression expr, params CodeExpression[] indices) + { + return new CodeIndexerExpression(expr, indices); + } + + public static CodeFieldReferenceExpression Field(this CodeExpression expr, string name) + { + return new CodeFieldReferenceExpression(expr, name); + } + + public static CodePropertyReferenceExpression Property(this CodeExpression expr, string name) + { + return new CodePropertyReferenceExpression(expr, name); + } + + public static CodeNamespace AddNamespace(this CodeCompileUnit ccu, string name) + { + CodeNamespace n = new CodeNamespace(name); + ccu.Namespaces.Add(n); + return n; + } + + public static CodeTypeDeclaration AddType(this CodeNamespace ns, string name) + { + CodeTypeDeclaration n = new CodeTypeDeclaration(name); + ns.Types.Add(n); + return n; + } + + public static CodeNamespaceImport AddImport(this CodeNamespace ns, string nameSpace) + { + CodeNamespaceImport cni = new CodeNamespaceImport(nameSpace); + ns.Imports.Add(cni); + return cni; + } + + public static CodeMemberField AddField(this CodeTypeDeclaration typeDecl, Type type, string name) + { + return typeDecl.AddField(Easy.TypeRef(type), name); + } + public static CodeMemberField AddField(this CodeTypeDeclaration typeDecl, CodeTypeReference type, string name) + { + CodeMemberField f = new CodeMemberField(type, name); + typeDecl.Members.Add(f); + return f; + } + + public static EasyProperty AddProperty(this CodeTypeDeclaration typeDecl, Type type, string name) + { + return AddProperty(typeDecl, Easy.TypeRef(type), name); + } + public static EasyProperty AddProperty(this CodeTypeDeclaration typeDecl, CodeTypeReference type, string name) + { + EasyProperty p = new EasyProperty(type, name); + typeDecl.Members.Add(p); + if (typeDecl.IsInterface == false) { + p.Attributes = MemberAttributes.Public | MemberAttributes.Final; + } + return p; + } + + public static EasyProperty AddProperty(this CodeTypeDeclaration typeDecl, CodeMemberField field, string name) + { + EasyProperty p = AddProperty(typeDecl, field.Type, name); + p.Getter.Return(new CodeVariableReferenceExpression(field.Name)); + p.Attributes |= field.Attributes & MemberAttributes.Static; // copy static flag + return p; + } + + /// + /// Adds a method with return type void and attributes=Public|Final to this type. + /// + public static EasyMethod AddMethod(this CodeTypeDeclaration typeDecl, string name) + { + return AddMethod(typeDecl, Easy.TypeRef(typeof(void)), name); + } + /// + /// Adds a method with return type and attributes=Public|Final to this type. + /// + public static EasyMethod AddMethod(this CodeTypeDeclaration typeDecl, Type type, string name) + { + return AddMethod(typeDecl, Easy.TypeRef(type), name); + } + /// + /// Adds a method with return type and attributes=Public|Final to this type. + /// + public static EasyMethod AddMethod(this CodeTypeDeclaration typeDecl, CodeTypeReference type, string name) + { + EasyMethod p = new EasyMethod(type, name); + typeDecl.Members.Add(p); + if (typeDecl.IsInterface == false) { + p.Attributes = MemberAttributes.Public | MemberAttributes.Final; + } + return p; + } + + public static CodeAttributeDeclaration AddAttribute(this CodeTypeMember typeMember, Type type, params CodeExpression[] arguments) + { + return Easy.AddAttribute(typeMember.CustomAttributes, Easy.TypeRef(type), arguments); + } + public static CodeAttributeDeclaration AddAttribute(this CodeTypeMember typeMember, CodeTypeReference type, params CodeExpression[] arguments) + { + return Easy.AddAttribute(typeMember.CustomAttributes, type, arguments); + } + } + + public class EasyProperty : CodeMemberProperty + { + EasyBlock getter, setter; + + public EasyProperty() + { + getter = new EasyBlock(this.GetStatements); + setter = new EasyBlock(this.SetStatements); + } + + public EasyProperty(CodeTypeReference type, string name) : this() + { + this.Type = type; + this.Name = name; + } + + public EasyBlock Getter { + get { return getter; } + } + + public EasyBlock Setter { + get { return setter; } + } + } + + public class EasyMethod : CodeMemberMethod + { + EasyBlock body; + + public EasyMethod() + { + body = new EasyBlock(this.Statements); + } + + public EasyMethod(CodeTypeReference type, string name) : this() + { + this.ReturnType = type; + this.Name = name; + } + + public CodeParameterDeclarationExpression AddParameter(Type type, string name) + { + return AddParameter(Easy.TypeRef(type), name); + } + public CodeParameterDeclarationExpression AddParameter(CodeTypeReference type, string name) + { + CodeParameterDeclarationExpression cpde; + cpde = new CodeParameterDeclarationExpression(type, name); + this.Parameters.Add(cpde); + return cpde; + } + + public EasyBlock Body { + get { return body; } + } + } + + public sealed class EasyBlock + { + readonly CodeStatementCollection csc; + + public EasyBlock(CodeStatementCollection csc) + { + this.csc = csc; + } + + public CodeMethodReturnStatement Return(CodeExpression expr) + { + CodeMethodReturnStatement st = new CodeMethodReturnStatement(expr); + csc.Add(st); + return st; + } + + public CodeThrowExceptionStatement Throw(CodeExpression expr) + { + CodeThrowExceptionStatement st = new CodeThrowExceptionStatement(expr); + csc.Add(st); + return st; + } + + public CodeAssignStatement Assign(CodeExpression lhs, CodeExpression rhs) + { + CodeAssignStatement st = new CodeAssignStatement(lhs, rhs); + csc.Add(st); + return st; + } + + /// + /// Execute one expression as statement. + /// + public CodeExpressionStatement Add(CodeExpression expr) + { + CodeExpressionStatement st = new CodeExpressionStatement(expr); + csc.Add(st); + return st; + } + + /// + /// Adds the statement. + /// + public CodeStatement Add(CodeStatement st) + { + csc.Add(st); + return st; + } + + /// + /// Invoke a method on target as statement. + /// + public CodeExpressionStatement InvokeMethod(CodeExpression target, string name, params CodeExpression[] arguments) + { + return Add(new CodeMethodInvokeExpression(target, name, arguments)); + } + + /// + /// Declares a local variable. + /// + public CodeVariableDeclarationStatement DeclareVariable(Type type, string name) + { + return DeclareVariable(Easy.TypeRef(type), name); + } + /// + /// Declares a local variable. + /// + public CodeVariableDeclarationStatement DeclareVariable(CodeTypeReference type, string name) + { + CodeVariableDeclarationStatement st = new CodeVariableDeclarationStatement(type, name); + csc.Add(st); + return st; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ExpressionContext.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ExpressionContext.cs new file mode 100644 index 000000000..88851bfe3 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ExpressionContext.cs @@ -0,0 +1,486 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Class describing a context in which an expression can be. + /// Serves as filter for code completion results, but the contexts exposed as static fields + /// can also be used as a kind of enumeration for special behaviour in the resolver. + /// + public abstract class ExpressionContext + { + #region Instance members + public abstract bool ShowEntry(ICompletionEntry o); + + protected bool readOnly = true; + object suggestedItem; + + /// + /// Gets if the expression is in the context of an object creation. + /// + public virtual bool IsObjectCreation { + get { + return false; + } + set { + if (value) + throw new NotSupportedException(); + } + } + + /// + /// Gets/Sets the default item that should be included in a code completion popup + /// in this context and selected as default value. + /// + /// + /// "List<TypeName> var = new *expr*();" has as suggested item the pseudo-class + /// "List<TypeName>". + /// + public object SuggestedItem { + get { + return suggestedItem; + } + set { + if (readOnly) + throw new NotSupportedException(); + suggestedItem = value; + } + } + + public virtual bool IsTypeContext { + get { return false; } + } + #endregion + + #region VB specific contexts (public static fields) * MOVE TO ANOTHER CLASS * + /// The context expects a new parameter declaration + /// Function Test(*expr*, *expr*, ...) + public static readonly ExpressionContext Parameter = new DefaultExpressionContext("Parameter"); + #endregion + + #region Default contexts (public static fields) + /// Default/unknown context + public readonly static ExpressionContext Default = new DefaultExpressionContext("Default"); + + /// The context expects the base type of an enum. + /// enum Name : *expr* {} + public readonly static ExpressionContext EnumBaseType = new EnumBaseTypeExpressionContext(); + + /// Context expects a non-sealed type or interface + /// class C : *expr* {} + public readonly static ExpressionContext InheritableType = new InheritableTypeExpressionContext(); + + /// Context expects a namespace name + /// using *expr*; + public readonly static ExpressionContext Namespace = new ImportableExpressionContext(false); + + /// Context expects an importable type (namespace or class with public static members) + /// Imports *expr*; + public readonly static ExpressionContext Importable = new ImportableExpressionContext(true); + + /// Context expects a type name + /// typeof(*expr*) + public readonly static ExpressionContext Type = new TypeExpressionContext(null, false, true); + + /// Context expects the name of a non-static, non-void type + /// is *expr*, *expr* variableName + public readonly static ExpressionContext NonStaticNonVoidType = new NonStaticTypeExpressionContext("NonStaticType", false); + + /// Context expects a non-abstract type that has accessible constructors + /// new *expr*(); + /// When using this context, a resolver should treat the expression as object creation, + /// even when the keyword "new" is not part of the expression. + public readonly static ExpressionContext ObjectCreation = new TypeExpressionContext(null, true, true); + + /// Context expects a type deriving from System.Attribute. + /// [*expr*()] + /// When using this context, a resolver should try resolving typenames with an + /// appended "Attribute" suffix and treat "invocations" of the attribute type as + /// object creation. + public readonly static ExpressionContext Attribute = new AttributeExpressionContext(); + + /// Context expects a type name which has special base type + /// The class the expression must derive from. + /// Specifies whether classes must be constructable. + /// catch(*expr* ...), using(*expr* ...), throw new *expr*(); + public static ExpressionContext TypeDerivingFrom(IReturnType baseType, bool isObjectCreation) + { + return new TypeExpressionContext(baseType, isObjectCreation, false); + } + + /// Context expects an interface + /// interface C : *expr* {} + /// Implements *expr* + public readonly static ExpressionContext Interface = new ClassTypeExpressionContext(ClassType.Interface); + + /// Context expects a delegate + /// public event *expr* + public readonly static ExpressionContext DelegateType = new ClassTypeExpressionContext(ClassType.Delegate); + + /// The context expects a new identifier + /// class *expr* {}; string *expr*; + public readonly static ExpressionContext IdentifierExpected = new DefaultExpressionContext("IdentifierExpected"); + + /// The context is outside of any type declaration, expecting a global-level keyword. + public readonly static ExpressionContext Global = new DefaultExpressionContext("Global"); + + /// The context is the body of a type declaration. + public readonly static ExpressionContext TypeDeclaration = new ExpressionContext.NonStaticTypeExpressionContext("TypeDeclaration", true); + + /// The context is the body of a method. + /// void Main () { *expr* } + public readonly static ExpressionContext MethodBody = new ExpressionContext.DefaultExpressionContext("MethodBody"); + #endregion + + #region DefaultExpressionContext + internal sealed class DefaultExpressionContext : ExpressionContext + { + string name; + + public DefaultExpressionContext(string name) + { + this.name = name; + } + + public override bool ShowEntry(ICompletionEntry o) + { + return true; + } + + public override string ToString() + { + return "[" + GetType().Name + ": " + name + "]"; + } + } + #endregion + + #region NamespaceExpressionContext + sealed class ImportableExpressionContext : ExpressionContext + { + bool allowImportClasses; + + public ImportableExpressionContext(bool allowImportClasses) + { + this.allowImportClasses = allowImportClasses; + } + + public override bool ShowEntry(ICompletionEntry o) + { + if (!(o is IEntity)) + return true; + IClass c = o as IClass; + if (allowImportClasses && c != null) { + return c.HasPublicOrInternalStaticMembers; + } + return false; + } + + public override string ToString() + { + return "[" + GetType().Name + " AllowImportClasses=" + allowImportClasses.ToString() + "]"; + } + } + #endregion + + #region TypeExpressionContext + sealed class TypeExpressionContext : ExpressionContext + { + IClass baseClass; + bool isObjectCreation; + + public TypeExpressionContext(IReturnType baseType, bool isObjectCreation, bool readOnly) + { + if (baseType != null) + baseClass = baseType.GetUnderlyingClass(); + this.isObjectCreation = isObjectCreation; + this.readOnly = readOnly; + } + + public override bool ShowEntry(ICompletionEntry o) + { + if (!(o is IEntity)) + return true; + IClass c = o as IClass; + if (c == null) + return false; + if (isObjectCreation) { + if (c.IsAbstract || c.IsStatic) return false; + if (c.ClassType == ClassType.Enum || c.ClassType == ClassType.Interface) + return false; + } + if (baseClass == null) + return true; + return c.IsTypeInInheritanceTree(baseClass); + } + + public override bool IsObjectCreation { + get { + return isObjectCreation; + } + set { + if (readOnly && value != isObjectCreation) + throw new NotSupportedException(); + isObjectCreation = value; + } + } + + public override bool IsTypeContext { + get { return true; } + } + + public override string ToString() + { + if (baseClass != null) + return "[" + GetType().Name + ": " + baseClass.FullyQualifiedName + + " IsObjectCreation=" + IsObjectCreation + "]"; + else + return "[" + GetType().Name + " IsObjectCreation=" + IsObjectCreation + "]"; + } + + public override bool Equals(object obj) + { + TypeExpressionContext o = obj as TypeExpressionContext; + return o != null && object.Equals(baseClass, o.baseClass) && IsObjectCreation == o.IsObjectCreation; + } + + public override int GetHashCode() + { + return ((baseClass != null) ? baseClass.GetHashCode() : 0) + ^ isObjectCreation.GetHashCode(); + } + } + #endregion + + #region CombinedExpressionContext + public static ExpressionContext operator | (ExpressionContext a, ExpressionContext b) + { + return new CombinedExpressionContext(0, a, b); + } + + public static ExpressionContext operator & (ExpressionContext a, ExpressionContext b) + { + return new CombinedExpressionContext(1, a, b); + } + + public static ExpressionContext operator ^ (ExpressionContext a, ExpressionContext b) + { + return new CombinedExpressionContext(2, a, b); + } + + sealed class CombinedExpressionContext : ExpressionContext + { + byte opType; // 0 = or ; 1 = and ; 2 = xor + ExpressionContext a; + ExpressionContext b; + + public CombinedExpressionContext(byte opType, ExpressionContext a, ExpressionContext b) + { + if (a == null) + throw new ArgumentNullException("a"); + if (b == null) + throw new ArgumentNullException("a"); + this.opType = opType; + this.a = a; + this.b = b; + } + + public override bool ShowEntry(ICompletionEntry o) + { + if (opType == 0) + return a.ShowEntry(o) || b.ShowEntry(o); + else if (opType == 1) + return a.ShowEntry(o) && b.ShowEntry(o); + else + return a.ShowEntry(o) ^ b.ShowEntry(o); + } + + public override string ToString() + { + string op; + if (opType == 0) + op = " OR "; + else if (opType == 1) + op = " AND "; + else + op = " XOR "; + return "[" + GetType().Name + ": " + a + op + b + "]"; + } + + public override int GetHashCode() + { + int hashCode = 0; + unchecked { + hashCode += opType.GetHashCode(); + if (a != null) hashCode += a.GetHashCode() * 3; + if (b != null) hashCode += b.GetHashCode() * 181247123; + } + return hashCode; + } + + public override bool Equals(object obj) + { + CombinedExpressionContext cec = obj as CombinedExpressionContext; + return cec != null && this.opType == cec.opType && object.Equals(this.a, cec.a) && object.Equals(this.b, cec.b); + } + } + #endregion + + #region EnumBaseTypeExpressionContext + sealed class EnumBaseTypeExpressionContext : ExpressionContext + { + public override bool ShowEntry(ICompletionEntry o) + { + IClass c = o as IClass; + if (c != null) { + // use this hack to show dummy classes like "short" + // (go from the dummy class to the real class) + if (c.Methods.Count > 0) { + c = c.Methods[0].DeclaringType; + } + switch (c.FullyQualifiedName) { + case "System.Byte": + case "System.SByte": + case "System.Int16": + case "System.UInt16": + case "System.Int32": + case "System.UInt32": + case "System.Int64": + case "System.UInt64": + return true; + default: + return false; + } + } else { + return false; + } + } + + public override string ToString() + { + return "[" + GetType().Name + "]"; + } + } + #endregion + + #region AttributeExpressionContext + sealed class AttributeExpressionContext : ExpressionContext + { + public override bool ShowEntry(ICompletionEntry o) + { + if (!(o is IEntity)) + return true; + IClass c = o as IClass; + if (c != null && !c.IsAbstract) { + return c.IsTypeInInheritanceTree(c.ProjectContent.SystemTypes.Attribute.GetUnderlyingClass()); + } else { + return false; + } + } + + public override bool IsTypeContext { + get { return true; } + } + + public override string ToString() + { + return "[" + GetType().Name + "]"; + } + } + #endregion + + #region InheritableTypeExpressionContext + sealed class InheritableTypeExpressionContext : ExpressionContext + { + public override bool ShowEntry(ICompletionEntry o) + { + if (!(o is IEntity)) return true; + IClass c = o as IClass; + if (c != null) { + foreach (IClass innerClass in c.InnerClasses) { + if (ShowEntry(innerClass)) return true; + } + if (c.ClassType == ClassType.Interface) return true; + if (c.ClassType == ClassType.Class) { + if (!c.IsSealed && !c.IsStatic) return true; + } + } + return false; + } + + public override string ToString() + { + return "[" + GetType().Name + "]"; + } + } + #endregion + + #region ClassTypeExpressionContext + sealed class ClassTypeExpressionContext : ExpressionContext + { + readonly ClassType expectedType; + + public ClassTypeExpressionContext(ClassType expectedType) + { + this.expectedType = expectedType; + } + + public override bool ShowEntry(ICompletionEntry o) + { + if (!(o is IEntity)) return true; + IClass c = o as IClass; + if (c != null) { + foreach (IClass innerClass in c.InnerClasses) { + if (ShowEntry(innerClass)) return true; + } + if (c.ClassType == expectedType) return true; + } + return false; + } + + public override string ToString() + { + return "[" + GetType().Name + " expectedType=" + expectedType.ToString() + "]"; + } + } + #endregion + + #region NonStaticTypeExpressionContext + internal sealed class NonStaticTypeExpressionContext : ExpressionContext + { + string name; + bool allowVoid; + + public NonStaticTypeExpressionContext(string name, bool allowVoid) + { + this.name = name; + this.allowVoid = allowVoid; + } + + public override bool ShowEntry(ICompletionEntry o) + { + if (!(o is IEntity)) return true; + IClass c = o as IClass; + if (c != null) { + if (!allowVoid) { + if (c.FullyQualifiedName == "System.Void" || c.FullyQualifiedName == "void") return false; + } + + foreach (IClass innerClass in c.InnerClasses) { + if (ShowEntry(innerClass)) return true; + } + if (!c.IsStatic && c.ClassType != ClassType.Module) return true; + } + return false; + } + + public override string ToString() + { + return "[" + GetType().Name + " " + name + "]"; + } + } + #endregion + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ExtensionMethods.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ExtensionMethods.cs new file mode 100644 index 000000000..3fcc231cb --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ExtensionMethods.cs @@ -0,0 +1,104 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.SharpDevelop.Dom +{ + static class ExtensionMethods + { + public static void AddRange(this ArrayList arrayList, IEnumerable elements) + { + foreach (object o in elements) + arrayList.Add(o); + } + + public static void AddRange(this ICollection list, IEnumerable elements) + { + foreach (T o in elements) + list.Add(o); + } + + /// + /// Converts a recursive data structure into a flat list. + /// + /// The root elements of the recursive data structure. + /// The function that gets the children of an element. + /// Iterator that enumerates the tree structure in preorder. + public static IEnumerable Flatten(this 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(); + } + } + } + + public static IEnumerable GetAllUsings(this ICompilationUnit cu) + { + return (new[]{cu.UsingScope}).Flatten(s=>s.ChildScopes).SelectMany(s=>s.Usings); + } + } + + /// + /// Publicly visible helper methods. + /// + public static class ExtensionMethodsPublic + { + // the difference between IClass and IReturnType is that IClass only contains the members + // that are declared in this very class, + // and IReturnType contains also members from base classes (including System.Object) and default (undeclared) constructors + + static SignatureComparer memberSignatureComparer = new SignatureComparer(); + + public static bool HasMember(this IClass containingClass, IMember member) + { + return containingClass.AllMembers.Any(m => memberSignatureComparer.Equals(member, m)); + } + + public static bool HasMember(this IReturnType containingClass, IMember member) + { + return containingClass.GetMembers().Any(m => memberSignatureComparer.Equals(member, m)); + } + + public static bool ImplementsInterface(this IClass targetClass, IClass requiredInterface) + { + var targetClassType = targetClass.GetCompoundClass().DefaultReturnType; + var requiredInterfaceType = requiredInterface.GetCompoundClass().DefaultReturnType; + // class.DefaultReturnType.GetMethods() returns also methods from base classes, default ctor, ToString() etc. etc. + return !requiredInterfaceType.GetMembers().Any(missingMember => !targetClassType.HasMember(missingMember)); + } + + public static bool ImplementsAbstractClass(this IClass targetClass, IClass abstractClass) + { + var requiredAbstractMembers = MemberLookupHelper.GetAccessibleMembers(abstractClass.DefaultReturnType, targetClass, LanguageProperties.CSharp, true).Where(m => m.IsAbstract); + return !requiredAbstractMembers.Any(missingMember => !targetClass.HasMember(missingMember)); + } + + public static IEnumerable GetMembers(this IReturnType typeReference) + { + var properties = typeReference.GetProperties().Cast(); + var methods = typeReference.GetMethods().Cast(); + var fields = typeReference.GetFields().Cast(); + var events = typeReference.GetEvents().Cast(); + return properties.Concat(methods).Concat(fields).Concat(events); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FilePosition.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FilePosition.cs new file mode 100644 index 000000000..4311ed594 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FilePosition.cs @@ -0,0 +1,114 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.NRefactory; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public struct FilePosition : IEquatable + { + string filename; + Location position; + ICompilationUnit compilationUnit; + + public static readonly FilePosition Empty = new FilePosition(null, Location.Empty); + + public FilePosition(ICompilationUnit compilationUnit, int line, int column) + { + this.position = new Location(column, line); + this.compilationUnit = compilationUnit; + if (compilationUnit != null) { + this.filename = compilationUnit.FileName; + } else { + this.filename = null; + } + } + + public FilePosition(string filename) + : this(filename, Location.Empty) + { + } + + public FilePosition(string filename, int line, int column) + : this(filename, new Location(column, line)) + { + } + + public FilePosition(string filename, Location position) + { + this.compilationUnit = null; + this.filename = filename; + this.position = position; + } + + public string FileName { + get { + return filename; + } + } + + public ICompilationUnit CompilationUnit { + get { + return compilationUnit; + } + } + + public Location Position { + get { + return position; + } + } + + public override string ToString() + { + return String.Format("{0} : (line {1}, col {2})", + filename, + Line, + Column); + } + + public int Line { + get { + return position.Y; + } + } + + public int Column { + get { + return position.X; + } + } + + public bool IsEmpty { + get { + return filename == null; + } + } + + public override bool Equals(object obj) + { + return obj is FilePosition && Equals((FilePosition)obj); + } + + public bool Equals(FilePosition other) + { + return this.FileName == other.FileName && this.Position == other.Position; + } + + public override int GetHashCode() + { + return filename.GetHashCode() ^ position.GetHashCode(); + } + + public static bool operator ==(FilePosition lhs, FilePosition rhs) + { + return lhs.Equals(rhs); + } + + public static bool operator !=(FilePosition lhs, FilePosition rhs) + { + return !lhs.Equals(rhs); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FileUtility.Minimal.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FileUtility.Minimal.cs new file mode 100644 index 000000000..496b1501c --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FileUtility.Minimal.cs @@ -0,0 +1,112 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +using System; +using System.IO; +using System.Text; + +namespace ICSharpCode.Core +{ + /// + /// Description of FileUtility_Minimal. + /// + static class FileUtility + { + /// + /// Gets the normalized version of fileName. + /// Slashes are replaced with backslashes, backreferences "." and ".." are 'evaluated'. + /// + public static string NormalizePath(string fileName) + { + if (string.IsNullOrEmpty(fileName)) 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 = isWeb ? '/' : System.IO.Path.DirectorySeparatorChar; + + StringBuilder result = new StringBuilder(); + if (isWeb == false && fileName.StartsWith(@"\\") || fileName.StartsWith("//")) { + i = 2; + result.Append(outputSeparator); + } else { + i = 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) + // On unix, don't ignore empty segment if i==0 + if (isWeb || (i == 0 && Environment.OSVersion.Platform == PlatformID.Unix)) { + 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; + } + 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 (result.Length > 0 && result[result.Length - 1] == outputSeparator) { + result.Length -= 1; + } + if (result.Length == 2 && result[1] == ':') { + result.Append(outputSeparator); + } + } + return result.ToString(); + } + + 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) + Path.DirectorySeparatorChar; + testDirectory = NormalizePath(testDirectory) + Path.DirectorySeparatorChar; + + return testDirectory.StartsWith(baseDirectory, StringComparison.OrdinalIgnoreCase); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FoldingRegion.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FoldingRegion.cs new file mode 100644 index 000000000..c86c5e3b9 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FoldingRegion.cs @@ -0,0 +1,31 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public sealed class FoldingRegion : Immutable + { + string name; + DomRegion region; + + public string Name { + get { + return name; + } + } + + public DomRegion Region { + get { + return region; + } + } + + public FoldingRegion(string name, DomRegion region) + { + this.name = name; + this.region = region; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FusionNative.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FusionNative.cs new file mode 100644 index 000000000..e6c6a968e --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/FusionNative.cs @@ -0,0 +1,275 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; + +namespace MSjogren.GacTool.FusionNative +{ + [ComImport(), Guid("E707DCDE-D1CD-11D2-BAB9-00C04F8ECEAE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAssemblyCache + { + [PreserveSig()] + int UninstallAssembly(uint dwFlags, + [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, + IntPtr pvReserved, + out uint pulDisposition); + + [PreserveSig()] + int QueryAssemblyInfo(uint dwFlags, + [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, + IntPtr pAsmInfo); + + [PreserveSig()] + int CreateAssemblyCacheItem(uint dwFlags, + IntPtr pvReserved, + out IAssemblyCacheItem ppAsmItem, + [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName); + + [PreserveSig()] + int CreateAssemblyScavenger(out object ppAsmScavenger); + + [PreserveSig()] + int InstallAssembly(uint dwFlags, + [MarshalAs(UnmanagedType.LPWStr)] string pszManifestFilePath, + IntPtr pvReserved); + } + + [ComImport(), Guid("9E3AAEB4-D1CD-11D2-BAB9-00C04F8ECEAE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAssemblyCacheItem + { + void CreateStream([MarshalAs(UnmanagedType.LPWStr)] string pszName, + uint dwFormat, + uint dwFlags, + uint dwMaxSize, + out IStream ppStream); + + void IsNameEqual(IAssemblyName pName); + + void Commit(uint dwFlags); + + void MarkAssemblyVisible(uint dwFlags); + } + + [ComImport(), Guid("CD193BC0-B4BC-11D2-9833-00C04FC31D2E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAssemblyName + { + // + // Assembly name properties + // 0 = PublicKey, byte[]* ; ??? + // 1 = PublicKeyToken, byte[8]* + // 3 = Assembly Name, LPWSTR + // 4 = Major Version, ushort* + // 5 = Minor Version, ushort* + // 6 = Build Number, ushort* + // 7 = Revison Number, ushort* + // 8 = Culture, LPWSTR + // 9 = Processor Type, ??? ; ??? + // 10 = OS Type, ??? ; ??? + // 13 = Codebase, LPWSTR + // 14 = Modified Date, FILETIME* ; Only for Downloaded assemblies ? + // 17 = Custom, LPWSTR ; ZAP string, only for NGEN assemblies + // 19 = MVID, byte[16]* ; MVID value from __AssemblyInfo__.ini - what's this? + // + [PreserveSig()] + int Set(uint PropertyId, + IntPtr pvProperty, + uint cbProperty); + + [PreserveSig()] + int Get(uint PropertyId, + IntPtr pvProperty, + ref uint pcbProperty); + + [PreserveSig()] + int Finalize(); + + [PreserveSig()] + int GetDisplayName([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder szDisplayName, + ref uint pccDisplayName, + uint dwDisplayFlags); + + [PreserveSig()] + int BindToObject(object refIID, + object pAsmBindSink, + IApplicationContext pApplicationContext, + [MarshalAs(UnmanagedType.LPWStr)] string szCodeBase, + long llFlags, + int pvReserved, + uint cbReserved, + out int ppv); + + [PreserveSig()] + int GetName(ref uint lpcwBuffer, + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzName); + + [PreserveSig()] + int GetVersion(out uint pdwVersionHi, + out uint pdwVersionLow); + + [PreserveSig()] + int IsEqual(IAssemblyName pName, + uint dwCmpFlags); + + [PreserveSig()] + int Clone(out IAssemblyName pName); + } + + [ComImport(), Guid("7C23FF90-33AF-11D3-95DA-00A024A85B51"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IApplicationContext + { + void SetContextNameObject(IAssemblyName pName); + + void GetContextNameObject(out IAssemblyName ppName); + + void Set([MarshalAs(UnmanagedType.LPWStr)] string szName, + int pvValue, + uint cbValue, + uint dwFlags); + + void Get([MarshalAs(UnmanagedType.LPWStr)] string szName, + out int pvValue, + ref uint pcbValue, + uint dwFlags); + + void GetDynamicDirectory(out int wzDynamicDir, + ref uint pdwSize); + } + + [ComImport(), Guid("21B8916C-F28E-11D2-A473-00C04F8EF448"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAssemblyEnum + { + [PreserveSig()] + int GetNextAssembly(out IApplicationContext ppAppCtx, + out IAssemblyName ppName, + uint dwFlags); + + [PreserveSig()] + int Reset(); + + [PreserveSig()] + int Clone(out IAssemblyEnum ppEnum); + } + + + [ComImport(), Guid("1D23DF4D-A1E2-4B8B-93D6-6EA3DC285A54"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IHistoryReader + { + [PreserveSig()] + int GetFilePath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzFilePath, + ref uint pdwSize); + + [PreserveSig()] + int GetApplicationName([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzAppName, + ref uint pdwSize); + + [PreserveSig()] + int GetEXEModulePath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzExePath, + ref uint pdwSize); + + void GetNumActivations(out uint pdwNumActivations); + + void GetActivationDate(uint dwIdx, // One-based! + out long /* FILETIME */ pftDate); + + [PreserveSig()] + int GetRunTimeVersion(ref long /* FILETIME */ pftActivationDate, + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzRunTimeVersion, + ref uint pdwSize); + + void GetNumAssemblies(ref long /* FILETIME */ pftActivationDate, + out uint pdwNumAsms); + + void GetHistoryAssembly(ref long /* FILETIME */ pftActivationDate, + uint dwIdx, // One-based! + [MarshalAs(UnmanagedType.IUnknown)] out object ppHistAsm); + + } + + internal static class Fusion + { + [DllImport("fusion.dll", CharSet=CharSet.Auto)] + internal static extern int CreateAssemblyCache(out IAssemblyCache ppAsmCache, + uint dwReserved); + + + // + // dwFlags: 1 = Enumerate native image (NGEN) assemblies + // 2 = Enumerate GAC assemblies + // 4 = Enumerate Downloaded assemblies ??? + // + [DllImport("fusion.dll", CharSet=CharSet.Auto)] + internal static extern int CreateAssemblyEnum(out IAssemblyEnum ppEnum, + IApplicationContext pAppCtx, + IAssemblyName pName, + uint dwFlags, + int pvReserved); + + [DllImport("fusion.dll", CharSet=CharSet.Auto)] + internal static extern int CreateAssemblyNameObject(out IAssemblyName ppName, + string szAssemblyName, + uint dwFlags, + int pvReserved); + + [DllImport("fusion.dll", CharSet=CharSet.Auto)] + internal static extern int CreateHistoryReader(string wzFilePath, + out IHistoryReader ppHistReader); + + // Retrieves the path of the ApplicationHistory folder, typically + // Documents and Settings\\Local Settings\Application Data\ApplicationHistory + // containing .ini files that can be read with IHistoryReader. + // pwdSize appears to be the offset of the last backslash in the returned + // string after the call. + // Returns S_OK on success, error HRESULT on failure. + // + [DllImport("fusion.dll", CharSet=CharSet.Unicode)] + internal static extern int GetHistoryFileDirectory([MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzDir, + ref uint pdwSize); + + [DllImport("fusion.dll")] + internal static extern int NukeDownloadedCache(); + + // ????? + [DllImport("fusion.dll")] + internal static extern int CreateApplicationContext(out IApplicationContext ppAppContext, + uint dw); + + [DllImport("fusion.dll")] + internal static extern int GetCachePath(uint flags, + [MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzDir, + ref uint pdwSize); + + public static string GetGacPath(bool isCLRv4 = false) + { + const uint ASM_CACHE_ROOT = 0x08; // CLR V2.0 + const uint ASM_CACHE_ROOT_EX = 0x80; // CLR V4.0 + uint flags = isCLRv4 ? ASM_CACHE_ROOT_EX : ASM_CACHE_ROOT; + + const int size = 260; // MAX_PATH + StringBuilder b = new StringBuilder(size); + uint tmp = size; + GetCachePath(flags, b, ref tmp); + return b.ToString(); + } + + // _InstallCustomAssembly@16 + // _InstallCustomModule@8 + // _LookupHistoryAssembly@28 + // _PreBindAssembly@20 + // _CreateInstallReferenceEnum@16 + + + // + // Brings up the .NET Applicaion Restore wizard + // Returns S_OK, 0x80131075 (App not run) or 0x80131087 (Fix failed) + // + [DllImport("shfusion.dll", CharSet=CharSet.Unicode)] + internal static extern uint PolicyManager(IntPtr hWndParent, + string pwzFullyQualifiedAppPath, + string pwzAppName, + int dwFlags); + + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/GacInterop.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/GacInterop.cs new file mode 100644 index 000000000..0d7e9be79 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/GacInterop.cs @@ -0,0 +1,139 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +using MSjogren.GacTool.FusionNative; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Class with static members to access the content of the global assembly + /// cache. + /// + public static class GacInterop + { + volatile static string cachedGacPathV2; + volatile static string cachedGacPathV4; + + public static string GacRootPathV2 { + get { + if (cachedGacPathV2 == null) { + cachedGacPathV2 = Fusion.GetGacPath(false); + } + return cachedGacPathV2; + } + } + + public static string GacRootPathV4 { + get { + if (cachedGacPathV4 == null) { + cachedGacPathV4 = Fusion.GetGacPath(true); + } + return cachedGacPathV4; + } + } + + public static bool IsWithinGac(string assemblyLocation) + { + return Core.FileUtility.IsBaseDirectory(GacRootPathV2, assemblyLocation) + || Core.FileUtility.IsBaseDirectory(GacRootPathV4, assemblyLocation); + } + + public static List GetAssemblyList() + { + IApplicationContext applicationContext = null; + IAssemblyEnum assemblyEnum = null; + IAssemblyName assemblyName = null; + + List l = new List(); + Fusion.CreateAssemblyEnum(out assemblyEnum, null, null, 2, 0); + while (assemblyEnum.GetNextAssembly(out applicationContext, out assemblyName, 0) == 0) { + uint nChars = 0; + assemblyName.GetDisplayName(null, ref nChars, 0); + + StringBuilder sb = new StringBuilder((int)nChars); + assemblyName.GetDisplayName(sb, ref nChars, 0); + + l.Add(new DomAssemblyName(sb.ToString())); + } + return l; + } + + /// + /// Gets the full display name of the GAC assembly of the specified short name + /// + public static DomAssemblyName FindBestMatchingAssemblyName(string name) + { + return FindBestMatchingAssemblyName(new DomAssemblyName(name)); + } + + public static DomAssemblyName FindBestMatchingAssemblyName(DomAssemblyName name) + { + string[] info; + Version requiredVersion = name.Version; + string publicKey = name.PublicKeyToken; + + IApplicationContext applicationContext = null; + IAssemblyEnum assemblyEnum = null; + IAssemblyName assemblyName; + Fusion.CreateAssemblyNameObject(out assemblyName, name.ShortName, 0, 0); + Fusion.CreateAssemblyEnum(out assemblyEnum, null, assemblyName, 2, 0); + List names = new List(); + + while (assemblyEnum.GetNextAssembly(out applicationContext, out assemblyName, 0) == 0) { + uint nChars = 0; + assemblyName.GetDisplayName(null, ref nChars, 0); + + StringBuilder sb = new StringBuilder((int)nChars); + assemblyName.GetDisplayName(sb, ref nChars, 0); + + string fullName = sb.ToString(); + if (publicKey != null) { + info = fullName.Split(','); + if (publicKey != info[3].Substring(info[3].LastIndexOf('=') + 1)) { + // Assembly has wrong public key + continue; + } + } + names.Add(fullName); + } + if (names.Count == 0) + return null; + string best = null; + Version bestVersion = null; + Version currentVersion; + if (requiredVersion != null) { + // use assembly with lowest version higher or equal to required version + for (int i = 0; i < names.Count; i++) { + info = names[i].Split(','); + currentVersion = new Version(info[1].Substring(info[1].LastIndexOf('=') + 1)); + if (currentVersion.CompareTo(requiredVersion) < 0) + continue; // version not good enough + if (best == null || currentVersion.CompareTo(bestVersion) < 0) { + bestVersion = currentVersion; + best = names[i]; + } + } + if (best != null) + return new DomAssemblyName(best); + } + // use assembly with highest version + best = names[0]; + info = names[0].Split(','); + bestVersion = new Version(info[1].Substring(info[1].LastIndexOf('=') + 1)); + for (int i = 1; i < names.Count; i++) { + info = names[i].Split(','); + currentVersion = new Version(info[1].Substring(info[1].LastIndexOf('=') + 1)); + if (currentVersion.CompareTo(bestVersion) > 0) { + bestVersion = currentVersion; + best = names[i]; + } + } + return new DomAssemblyName(best); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/HostCallback.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/HostCallback.cs new file mode 100644 index 000000000..861d46397 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/HostCallback.cs @@ -0,0 +1,62 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.SharpDevelop.Dom.Refactoring; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// A class containing static actions that should be overridden by the + /// application using ICSharpCode.SharpDevelop.Dom. + /// + public static class HostCallback + { + /// + /// Show an error message. (string message, Exception ex) + /// + public static Action ShowError = delegate(string message, Exception ex) { + LoggingService.Error(message, ex); + throw new Exception(message, ex); + }; + + public static Action ShowMessage = delegate(string message) { + LoggingService.Info(message); + }; + + /// + /// Get the current project content. + /// + public static Func GetCurrentProjectContent = delegate { + throw new NotImplementedException("GetCurrentProjectContent was not implemented by the host."); + }; + + /// + /// Rename the member (first argument) to the new name (second argument). + /// Returns true on success, false on failure. + /// + public static Func RenameMember = delegate { + return false; + }; + + /// + /// Show error loading code-completion information. + /// The arguments are: string fileName, string include, string message + /// + public static Action ShowAssemblyLoadError = delegate {}; + + internal static void ShowAssemblyLoadErrorInternal(string fileName, string include, string message) + { + LoggingService.Warn("Error loading code-completion information for " + + include + " from " + fileName + + ":\r\n" + message + "\r\n"); + ShowAssemblyLoadError(fileName, include, message); + } + + /// + /// Initialize the code generator options of the passed CodeGenerator. + /// Invoked exactly once for each created instance of a class derived from CodeGenerator. + /// + public static Action InitializeCodeGeneratorOptions = delegate {}; + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/IComment.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/IComment.cs new file mode 100644 index 000000000..7aa76dd44 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/IComment.cs @@ -0,0 +1,24 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface IComment + { + bool IsBlockComment { + get; + } + + string CommentTag { + get; + } + + string CommentText { + get; + } + + DomRegion Region { + get; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/IExpressionFinder.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/IExpressionFinder.cs new file mode 100644 index 000000000..a49b45df6 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/IExpressionFinder.cs @@ -0,0 +1,66 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface IExpressionFinder + { + /// + /// Finds an expression before the current offset. + /// + ExpressionResult FindExpression(string text, int offset); + + /// + /// Finds an expression around the current offset. + /// + ExpressionResult FindFullExpression(string text, int offset); + + /// + /// Removed the last part of the expression. + /// + /// + /// "arr[i]" => "arr" + /// "obj.Field" => "obj" + /// "obj.Method(args,...)" => "obj.Method" + /// + string RemoveLastPart(string expression); + } + + /// + /// Structure containing the result of a call to an expression finder. + /// + public struct ExpressionResult + { + public static readonly ExpressionResult Empty = new ExpressionResult(null); + + /// The expression that has been found at the specified offset. + public string Expression; + /// The exact source code location of the expression. + public DomRegion Region; + /// Specifies the context in which the expression was found. + public ExpressionContext Context; + /// An object carrying additional language-dependend data. + public object Tag; + + public ExpressionResult(string expression) : this(expression, DomRegion.Empty, ExpressionContext.Default, null) {} + public ExpressionResult(string expression, ExpressionContext context) : this(expression, DomRegion.Empty, context, null) {} + + public ExpressionResult(string expression, DomRegion region, ExpressionContext context, object tag) + { + this.Expression = expression; + this.Region = region; + this.Context = context; + this.Tag = tag; + } + + public override string ToString() + { + if (Context == ExpressionContext.Default) + return "<" + Expression + ">"; + else + return "<" + Expression + "> (" + Context.ToString() + ")"; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/IResolver.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/IResolver.cs new file mode 100644 index 000000000..41a292e34 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/IResolver.cs @@ -0,0 +1,22 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Description of IResolver. + /// + public interface IResolver + { + /// + /// Resolves an expression. + /// The caretLineNumber and caretColumn is 1 based. + /// + ResolveResult Resolve(ExpressionResult expressionResult, + ParseInformation parseInfo, + string fileContent); + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractEntity.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractEntity.cs new file mode 100644 index 000000000..f7fd436d3 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractEntity.cs @@ -0,0 +1,358 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public abstract class AbstractEntity : AbstractFreezable, IEntity + { + ModifierEnum modifiers = ModifierEnum.None; + IList attributes; + DomRegion bodyRegion; + + IClass declaringType; + + string fullyQualifiedName; + string name; + string nspace; + + public AbstractEntity(IClass declaringType) + { + this.declaringType = declaringType; + } + + public AbstractEntity(IClass declaringType, string name) + { + this.declaringType = declaringType; + this.name = name; + if (declaringType != null) + nspace = declaringType.FullyQualifiedName; + + // lazy-computing the fully qualified name for class members saves ~7 MB RAM (when loading the SharpDevelop solution). + //fullyQualifiedName = nspace + '.' + name; + } + + public override string ToString() + { + return String.Format("[{0}: {1}]", GetType().Name, FullyQualifiedName); + } + + #region Naming + static readonly char[] nameDelimiters = { '.', '+' }; + + + public string FullyQualifiedName { + get { + if (fullyQualifiedName == null) { + if (name != null && nspace != null) { + fullyQualifiedName = nspace + '.' + name; + } else { + return String.Empty; + } + } + return fullyQualifiedName; + } + set { + CheckBeforeMutation(); + if (fullyQualifiedName == value) + return; + fullyQualifiedName = value; + name = null; + nspace = null; + OnFullyQualifiedNameChanged(EventArgs.Empty); + } + } + + protected virtual void OnFullyQualifiedNameChanged(EventArgs e) + { + } + + public virtual string DotNetName { + get { + if (this.DeclaringType != null) { + return this.DeclaringType.DotNetName + "." + this.Name; + } else { + return FullyQualifiedName; + } + } + } + + public string Name { + get { + if (name == null && FullyQualifiedName != null) { + int lastIndex = FullyQualifiedName.LastIndexOfAny(nameDelimiters); + + if (lastIndex < 0) { + name = FullyQualifiedName; + } else { + name = FullyQualifiedName.Substring(lastIndex + 1); + } + } + return name; + } + } + + public string Namespace { + get { + if (nspace == null && FullyQualifiedName != null) { + int lastIndex = FullyQualifiedName.LastIndexOf('.'); + + if (lastIndex < 0) { + nspace = String.Empty; + } else { + nspace = FullyQualifiedName.Substring(0, lastIndex); + } + } + return nspace; + } + } + + + #endregion + + protected override void FreezeInternal() + { + attributes = FreezeList(attributes); + base.FreezeInternal(); + } + + public IClass DeclaringType { + get { + return declaringType; + } + } + + public virtual DomRegion BodyRegion { + get { + return bodyRegion; + } + set { + CheckBeforeMutation(); + bodyRegion = value; + } + } + + public object UserData { get; set; } + + public IList Attributes { + get { + if (attributes == null) { + attributes = new List(); + } + return attributes; + } + set { + CheckBeforeMutation(); + attributes = value; + } + } + + string documentation; + + public string Documentation { + get { + if (documentation == null) { + string documentationTag = this.DocumentationTag; + if (documentationTag != null) { + IProjectContent pc = null; + if (this is IClass) { + pc = ((IClass)this).ProjectContent; + } else if (declaringType != null) { + pc = declaringType.ProjectContent; + } + if (pc != null) { + return pc.GetXmlDocumentation(documentationTag); + } + } + } + return documentation; + } + set { + CheckBeforeMutation(); + documentation = value; + } + } + + protected void CopyDocumentationFrom(IEntity entity) + { + AbstractEntity ae = entity as AbstractEntity; + if (ae != null) { + this.Documentation = ae.documentation; // do not cause pc.GetXmlDocumentation call for documentation copy + } else { + this.Documentation = entity.Documentation; + } + } + + public abstract string DocumentationTag { + get; + } + + #region Modifiers + public ModifierEnum Modifiers { + get { + return modifiers; + } + set { + CheckBeforeMutation(); + modifiers = value; + } + } + + public bool IsAbstract { + get { + return (modifiers & ModifierEnum.Abstract) == ModifierEnum.Abstract; + } + } + + public bool IsSealed { + get { + return (modifiers & ModifierEnum.Sealed) == ModifierEnum.Sealed; + } + } + + public bool IsStatic { + get { + return ((modifiers & ModifierEnum.Static) == ModifierEnum.Static) || IsConst; + } + } + + public bool IsConst { + get { + return (modifiers & ModifierEnum.Const) == ModifierEnum.Const; + } + } + + public bool IsVirtual { + get { + return (modifiers & ModifierEnum.Virtual) == ModifierEnum.Virtual; + } + } + + public bool IsPublic { + get { + return (modifiers & ModifierEnum.Public) == ModifierEnum.Public; + } + } + + public bool IsProtected { + get { + return (modifiers & ModifierEnum.Protected) == ModifierEnum.Protected; + } + } + + public bool IsPrivate { + get { + return (modifiers & ModifierEnum.Private) == ModifierEnum.Private; + } + } + + public bool IsInternal { + get { + return (modifiers & ModifierEnum.Internal) == ModifierEnum.Internal; + } + } + + [Obsolete("This property does not do what one would expect - it merely checks if protected+internal are set, it is not the equivalent of AssemblyAndFamily in Reflection!")] + public bool IsProtectedAndInternal { + get { + return (modifiers & (ModifierEnum.Internal | ModifierEnum.Protected)) == (ModifierEnum.Internal | ModifierEnum.Protected); + } + } + + [Obsolete("This property does not do what one would expect - it merely checks if one of protected+internal is set, it is not the equivalent of AssemblyOrFamily in Reflection!")] + public bool IsProtectedOrInternal { + get { + return IsProtected || IsInternal; + } + } + + public bool IsReadonly { + get { + return (modifiers & ModifierEnum.Readonly) == ModifierEnum.Readonly; + } + } + + public bool IsOverride { + get { + return (modifiers & ModifierEnum.Override) == ModifierEnum.Override; + } + } + public bool IsOverridable { + get { + return (IsOverride || IsVirtual || IsAbstract || + // Interface members have IsVirtual == IsAbstract == false. These properties are based on modifiers only. + (this.DeclaringType != null && this.DeclaringType.ClassType == ClassType.Interface)) + && !IsSealed; + } + } + public bool IsNew { + get { + return (modifiers & ModifierEnum.New) == ModifierEnum.New; + } + } + public bool IsSynthetic { + get { + return (modifiers & ModifierEnum.Synthetic) == ModifierEnum.Synthetic; + } + } + #endregion + + public bool IsAccessible(IClass callingClass, bool isAccessThoughReferenceOfCurrentClass) + { + if (IsPublic) { + return true; + } else if (IsInternal) { + // members can be both internal and protected: in that case, we want to return true if it is visible + // through any of the modifiers + if (callingClass != null && this.DeclaringType.ProjectContent.InternalsVisibleTo(callingClass.ProjectContent)) + return true; + } + // protected or private: + // search in callingClass and, if callingClass is a nested class, in its outer classes + while (callingClass != null) { + if (IsProtected) { + if (!isAccessThoughReferenceOfCurrentClass && !IsStatic) + return false; + return callingClass.IsTypeInInheritanceTree(this.DeclaringType); + } else { + // private + if (DeclaringType.FullyQualifiedName == callingClass.FullyQualifiedName + && DeclaringType.TypeParameters.Count == callingClass.TypeParameters.Count) + { + return true; + } + } + + callingClass = callingClass.DeclaringType; + } + return false; + } + + public abstract ICompilationUnit CompilationUnit { + get; + } + + public IProjectContent ProjectContent { + [System.Diagnostics.DebuggerStepThrough] + get { + return this.CompilationUnit.ProjectContent; + } + } + + public virtual int CompareTo(IEntity value) + { + return this.Modifiers - value.Modifiers; + } + + int IComparable.CompareTo(object value) + { + return CompareTo((IEntity)value); + } + + public abstract EntityType EntityType { + get; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractMember.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractMember.cs new file mode 100644 index 000000000..d7f0ad095 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractMember.cs @@ -0,0 +1,97 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public abstract class AbstractMember : AbstractEntity, IMember + { + IReturnType returnType; + DomRegion region; + IList interfaceImplementations; + IReturnType declaringTypeReference; + + protected override void FreezeInternal() + { + interfaceImplementations = FreezeList(interfaceImplementations); + base.FreezeInternal(); + } + + public sealed override ICompilationUnit CompilationUnit { + [System.Diagnostics.DebuggerStepThrough] + get { + return this.DeclaringType.CompilationUnit; + } + } + + public virtual DomRegion Region { + get { + return region; + } + set { + CheckBeforeMutation(); + region = value; + } + } + + public virtual IReturnType ReturnType { + get { + return returnType; + } + set { + CheckBeforeMutation(); + returnType = value; + } + } + + /// + /// Gets the declaring type reference (declaring type incl. type arguments) + /// + public virtual IReturnType DeclaringTypeReference { + get { + return declaringTypeReference ?? this.DeclaringType.DefaultReturnType; + } + set { + CheckBeforeMutation(); + declaringTypeReference = value; + } + } + + public IList InterfaceImplementations { + get { + return interfaceImplementations ?? (interfaceImplementations = new List()); + } + } + + public AbstractMember(IClass declaringType, string name) : base(declaringType, name) + { + // members must have a parent class + if (declaringType == null) + throw new ArgumentNullException("declaringType"); + } + + public abstract IMember Clone(); + + object ICloneable.Clone() + { + return this.Clone(); + } + + IMember genericMember; + + public virtual IMember GenericMember { + get { return genericMember; } + } + + public virtual IMember CreateSpecializedMember() + { + AbstractMember copy = Clone() as AbstractMember; + if (copy == null) + throw new Exception("Clone() must return an AbstractMember instance, or CreateSpecializedMember must also be overridden."); + copy.genericMember = this; + return copy; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractReturnType.cs new file mode 100644 index 000000000..3de3e91a1 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractReturnType.cs @@ -0,0 +1,136 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Abstract return type for return types that are not a . + /// + public abstract class AbstractReturnType : IReturnType + { + public abstract IClass GetUnderlyingClass(); + public abstract List GetMethods(); + public abstract List GetProperties(); + public abstract List GetFields(); + public abstract List GetEvents(); + + public virtual int TypeArgumentCount { + get { + return 0; + } + } + + public virtual bool Equals(IReturnType other) + { + if (other == null) + return false; + return other.IsDefaultReturnType && DefaultReturnType.Equals(this, other); + } + + public sealed override bool Equals(object o) + { + return Equals(o as IReturnType); + } + + public override int GetHashCode() + { + return DefaultReturnType.GetHashCode(this); + } + + string fullyQualifiedName = null; + + public virtual string FullyQualifiedName { + get { + if (fullyQualifiedName == null) { + return String.Empty; + } + return fullyQualifiedName; + } + set { + fullyQualifiedName = value; + } + } + + public virtual string Name { + get { + if (FullyQualifiedName == null) { + return null; + } + int index = FullyQualifiedName.LastIndexOf('.'); + return index < 0 ? FullyQualifiedName : FullyQualifiedName.Substring(index + 1); + } + } + + public virtual string Namespace { + get { + if (FullyQualifiedName == null) { + return null; + } + int index = FullyQualifiedName.LastIndexOf('.'); + return index < 0 ? String.Empty : FullyQualifiedName.Substring(0, index); + } + } + + public virtual string DotNetName { + get { + return FullyQualifiedName; + } + } + + public virtual bool IsDefaultReturnType { + get { + return true; + } + } + + public virtual bool IsArrayReturnType { + get { + return false; + } + } + public virtual ArrayReturnType CastToArrayReturnType() + { + return null; + } + + public virtual bool IsGenericReturnType { + get { + return false; + } + } + public virtual GenericReturnType CastToGenericReturnType() + { + return null; + } + + public virtual bool IsConstructedReturnType { + get { + return false; + } + } + public virtual ConstructedReturnType CastToConstructedReturnType() + { + return null; + } + + public bool IsDecoratingReturnType() where T : DecoratingReturnType + { + return false; + } + + public T CastToDecoratingReturnType() where T : DecoratingReturnType + { + return null; + } + + public virtual bool? IsReferenceType { get { return null; } } + + public virtual IReturnType GetDirectReturnType() + { + return this; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AnonymousMethodReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AnonymousMethodReturnType.cs new file mode 100644 index 000000000..932286ec0 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AnonymousMethodReturnType.cs @@ -0,0 +1,190 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// The return type of anonymous method expressions or lambda expressions. + /// + public class AnonymousMethodReturnType : DecoratingReturnType + { + IReturnType returnType; + IList parameters; + ICompilationUnit cu; + + public AnonymousMethodReturnType(ICompilationUnit cu) + { + this.cu = cu; + } + + public override bool Equals(IReturnType other) + { + if (other == null) return false; + AnonymousMethodReturnType o = other.CastToDecoratingReturnType(); + if (o == null) return false; + return this.FullyQualifiedName == o.FullyQualifiedName; + } + + public override int GetHashCode() + { + return this.FullyQualifiedName.GetHashCode(); + } + + public override T CastToDecoratingReturnType() + { + if (typeof(T) == typeof(AnonymousMethodReturnType)) { + return (T)(object)this; + } else { + return null; + } + } + + public IReturnType ToDefaultDelegate() + { + IReturnType type = new GetClassReturnType(cu.ProjectContent, "System.Func", 0); + List parameters = new List(); + + if (this.HasParameterList) + parameters = MethodParameters.Select(p => p.ReturnType ?? new GetClassReturnType(cu.ProjectContent, "System.Object", 0)).ToList(); + + if (this.MethodReturnType != null && this.MethodReturnType.FullyQualifiedName == "System.Void") + type = new GetClassReturnType(cu.ProjectContent, "System.Action", 0); + else { + var rt = this.MethodReturnType; + if (rt == null) + rt = new GetClassReturnType(cu.ProjectContent, "System.Object", 0); + parameters.Add(rt); + } + + return new ConstructedReturnType(type, parameters); + } + + /// + /// Return type of the anonymous method. Can be null if inferred from context. + /// + public IReturnType MethodReturnType { + get { return returnType; } + set { returnType = value; } + } + + public virtual IReturnType ResolveReturnType() + { + return returnType; + } + + public virtual IReturnType ResolveReturnType(IReturnType[] parameterTypes) + { + return returnType; + } + + /// + /// Gets the list of method parameters. Can be null if the anonymous method has no parameter list. + /// + public IList MethodParameters { + get { return parameters; } + set { parameters = value; } + } + + public virtual bool CanBeConvertedToExpressionTree { + get { return false; } + } + + public bool HasParameterList { + get { return parameters != null; } + } + + public bool HasImplicitlyTypedParameters { + get { + if (parameters == null) + return false; + else + return parameters.Any(p => p.ReturnType == null); + } + } + + DefaultClass cachedClass; + + public override IClass GetUnderlyingClass() + { + if (cachedClass != null) return cachedClass; + DefaultClass c = new DefaultClass(cu, ClassType.Delegate, ModifierEnum.None, DomRegion.Empty, null); + c.BaseTypes.Add(cu.ProjectContent.SystemTypes.Delegate); + AddDefaultDelegateMethod(c, returnType ?? cu.ProjectContent.SystemTypes.Object, parameters ?? new IParameter[0]); + cachedClass = c; + return c; + } + + internal static void AddDefaultDelegateMethod(DefaultClass c, IReturnType returnType, IList parameters) + { + ModifierEnum modifiers = ModifierEnum.Public | ModifierEnum.Synthetic; + DefaultMethod invokeMethod = new DefaultMethod("Invoke", returnType, modifiers, c.Region, DomRegion.Empty, c); + foreach (IParameter par in parameters) { + invokeMethod.Parameters.Add(par); + } + c.Methods.Add(invokeMethod); + invokeMethod = new DefaultMethod("BeginInvoke", c.ProjectContent.SystemTypes.IAsyncResult, modifiers, c.Region, DomRegion.Empty, c); + foreach (IParameter par in parameters) { + invokeMethod.Parameters.Add(par); + } + invokeMethod.Parameters.Add(new DefaultParameter("callback", c.ProjectContent.SystemTypes.AsyncCallback, DomRegion.Empty)); + invokeMethod.Parameters.Add(new DefaultParameter("object", c.ProjectContent.SystemTypes.Object, DomRegion.Empty)); + c.Methods.Add(invokeMethod); + invokeMethod = new DefaultMethod("EndInvoke", returnType, modifiers, c.Region, DomRegion.Empty, c); + invokeMethod.Parameters.Add(new DefaultParameter("result", c.ProjectContent.SystemTypes.IAsyncResult, DomRegion.Empty)); + c.Methods.Add(invokeMethod); + } + + public override IReturnType BaseType { + get { + return GetUnderlyingClass().DefaultReturnType; + } + } + + public override string Name { + get { + return "delegate"; + } + } + + public override string FullyQualifiedName { + get { + StringBuilder b = new StringBuilder("delegate"); + if (HasParameterList) { + bool first = true; + b.Append("("); + foreach (IParameter p in parameters) { + if (first) first = false; else b.Append(", "); + b.Append(p.Name); + if (p.ReturnType != null) { + b.Append(":"); + b.Append(p.ReturnType.Name); + } + } + b.Append(")"); + } + if (returnType != null) { + b.Append(":"); + b.Append(returnType.Name); + } + return b.ToString(); + } + } + + public override string Namespace { + get { + return ""; + } + } + + public override string DotNetName { + get { + return Name; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ArrayReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ArrayReturnType.cs new file mode 100644 index 000000000..6d611abb4 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ArrayReturnType.cs @@ -0,0 +1,154 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// The ArrayReturnType wraps around another type, converting it into an array + /// with the specified number of dimensions. + /// The element type is only used as return type for the indexer; all methods and fields + /// are retrieved from System.Array. + /// + public sealed class ArrayReturnType : DecoratingReturnType + { + IReturnType elementType; + int dimensions; + IProjectContent pc; + + internal IProjectContent ProjectContent { + get { + return pc; + } + } + + public ArrayReturnType(IProjectContent pc, IReturnType elementType, int dimensions) + { + if (pc == null) + throw new ArgumentNullException("pc"); + if (dimensions <= 0) + throw new ArgumentOutOfRangeException("dimensions", dimensions, "dimensions must be positive"); + if (elementType == null) + throw new ArgumentNullException("elementType"); + this.pc = pc; + this.elementType = elementType; + this.dimensions = dimensions; + } + + public override IReturnType GetDirectReturnType() + { + IReturnType newElementType = elementType.GetDirectReturnType(); + if (newElementType == elementType) + return this; + else + return new ArrayReturnType(pc, newElementType, dimensions); + } + + public override bool Equals(IReturnType rt) + { + if (rt == null || !rt.IsArrayReturnType) return false; + ArrayReturnType art = rt.CastToArrayReturnType(); + if (art.ArrayDimensions != dimensions) return false; + return elementType.Equals(art.ArrayElementType); + } + + public override int GetHashCode() + { + unchecked { + return 2 * elementType.GetHashCode() + 27 * dimensions; + } + } + + public override T CastToDecoratingReturnType() + { + if (typeof(T) == typeof(ArrayReturnType)) { + return (T)(object)this; + } else { + return null; + } + } + + public IReturnType ArrayElementType { + get { + return elementType; + } + } + + public int ArrayDimensions { + get { + return dimensions; + } + } + + public override string FullyQualifiedName { + get { + return elementType.FullyQualifiedName; + } + } + + public override string Name { + get { + return elementType.Name; + } + } + + public override string DotNetName { + get { + return AppendArrayString(elementType.DotNetName); + } + } + + public override IReturnType BaseType { + get { + return pc.SystemTypes.Array; + } + } + + /// + /// Indexer used exclusively for array return types + /// + public class ArrayIndexer : DefaultProperty + { + public ArrayIndexer(IReturnType elementType, IClass systemArray) + : base("Indexer", elementType, ModifierEnum.Public, DomRegion.Empty, DomRegion.Empty, systemArray) + { + IsIndexer = true; + } + } + + public override List GetProperties() + { + List l = base.GetProperties(); + ArrayIndexer property = new ArrayIndexer(elementType, this.BaseType.GetUnderlyingClass()); + IReturnType int32 = pc.SystemTypes.Int32; + for (int i = 0; i < dimensions; ++i) { + property.Parameters.Add(new DefaultParameter("index", int32, DomRegion.Empty)); + } + property.Freeze(); + l.Add(property); + return l; + } + + /// + /// Appends the array characters ([,,,]) to the string . + /// + string AppendArrayString(string a) + { + StringBuilder b = new StringBuilder(a, a.Length + 1 + dimensions); + b.Append('['); + for (int i = 1; i < dimensions; ++i) { + b.Append(','); + } + b.Append(']'); + return b.ToString(); + } + + public override string ToString() + { + return String.Format("[ArrayReturnType: {0}{1}]", elementType, AppendArrayString("")); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AttributeReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AttributeReturnType.cs new file mode 100644 index 000000000..c32975e1a --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AttributeReturnType.cs @@ -0,0 +1,47 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Linq; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Like SearchClassReturnType, but tries both the specified name and name+"Attribute". + /// + public class AttributeReturnType : ProxyReturnType + { + string name; + SearchClassReturnType scrt1, scrt2; + + public AttributeReturnType(ClassFinder context, string name) + { + if (context == null) + throw new ArgumentNullException("context"); + if (name == null) + throw new ArgumentNullException("name"); + this.name = name; + scrt1 = new SearchClassReturnType(context.ProjectContent, context.CallingClass, + context.CaretLine, context.CaretColumn, name, 0); + scrt2 = new SearchClassReturnType(context.ProjectContent, context.CallingClass, + context.CaretLine, context.CaretColumn, name + "Attribute", 0); + } + + public override IReturnType BaseType { + get { + IClass class1 = scrt1.GetUnderlyingClass(); + IClass class2 = scrt2.GetUnderlyingClass(); + if (class1 != null && class2 != null) { + if (class1.ClassInheritanceTree.Any(c => c.FullyQualifiedName == "System.Attribute")) + return scrt1; + else + return scrt2; + } else if (class2 != null) { + return scrt2; + } else { + return scrt1; + } + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/BoundTypeParameter.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/BoundTypeParameter.cs new file mode 100644 index 000000000..5d580d342 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/BoundTypeParameter.cs @@ -0,0 +1,91 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// A type parameter that was bound to a concrete type. + /// + public sealed class BoundTypeParameter : AbstractFreezable, ITypeParameter + { + readonly ITypeParameter baseTypeParameter; + readonly IMethod owningMethod; + readonly IClass owningClass; + IReturnType boundTo; + + public BoundTypeParameter(ITypeParameter baseTypeParameter, IClass owningClass) + : this(baseTypeParameter, owningClass, null) + { + } + + public BoundTypeParameter(ITypeParameter baseTypeParameter, IClass owningClass, IMethod owningMethod) + { + if (owningClass == null) + throw new ArgumentNullException("owningClass"); + if (baseTypeParameter == null) + throw new ArgumentNullException("baseTypeParameter"); + this.baseTypeParameter = baseTypeParameter; + this.owningMethod = owningMethod; + this.owningClass = owningClass; + } + + protected override void FreezeInternal() + { + base.FreezeInternal(); + baseTypeParameter.Freeze(); + owningMethod.Freeze(); + owningClass.Freeze(); + } + + public string Name { + get { return baseTypeParameter.Name; } + } + + public int Index { + get { return baseTypeParameter.Index; } + } + + public IList Attributes { + get { return baseTypeParameter.Attributes; } + } + + public IMethod Method { + get { return owningMethod; } + } + + public IClass Class { + get { return owningClass; } + } + + public IList Constraints { + get { return baseTypeParameter.Constraints; } + } + + public bool HasConstructableConstraint { + get { return baseTypeParameter.HasConstructableConstraint; } + } + + public bool HasReferenceTypeConstraint { + get { return baseTypeParameter.HasReferenceTypeConstraint; } + } + + public bool HasValueTypeConstraint { + get { return baseTypeParameter.HasValueTypeConstraint; } + } + + public IReturnType BoundTo { + get { return boundTo; } + set { + CheckBeforeMutation(); + boundTo = value; + } + } + + public ITypeParameter UnboundTypeParameter { + get { return baseTypeParameter.UnboundTypeParameter; } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CombinedReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CombinedReturnType.cs new file mode 100644 index 000000000..7f5c71e54 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CombinedReturnType.cs @@ -0,0 +1,152 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Combines multiple return types for use in constraints. + /// + public sealed class CombinedReturnType : AbstractReturnType + { + IList baseTypes; + + string fullName; + string name; + string @namespace; + string dotnetName; + + public override bool Equals(IReturnType obj) + { + CombinedReturnType combined = obj as CombinedReturnType; + if (combined == null) return false; + if (baseTypes.Count != combined.baseTypes.Count) return false; + for (int i = 0; i < baseTypes.Count; i++) { + if (!baseTypes[i].Equals(combined.baseTypes[i])) { + return false; + } + } + return true; + } + + public override int GetHashCode() + { + unchecked { + int res = 0; + foreach (IReturnType rt in baseTypes) { + res *= 1300027; + res += rt.GetHashCode(); + } + return res; + } + } + + public CombinedReturnType(IList baseTypes, string fullName, string name, string @namespace, string dotnetName) + { + this.baseTypes = baseTypes; + this.fullName = fullName; + this.name = name; + this.@namespace = @namespace; + this.dotnetName = dotnetName; + } + + public IList BaseTypes { + get { + return baseTypes; + } + } + + List Combine(Converter> conv) where T : IMember + { + int count = baseTypes.Count; + if (count == 0) + return null; + List list = null; + foreach (IReturnType baseType in baseTypes) { + List newList = conv(baseType); + if (newList == null) + continue; + if (list == null) { + list = newList; + } else { + foreach (T element in newList) { + bool found = false; + foreach (T t in list) { + if (t.CompareTo(element) == 0) { + found = true; + break; + } + } + if (!found) { + list.Add(element); + } + } + } + } + return list; + } + + public override List GetMethods() + { + return Combine(delegate(IReturnType type) { return type.GetMethods(); }); + } + + public override List GetProperties() + { + return Combine(delegate(IReturnType type) { return type.GetProperties(); }); + } + + public override List GetFields() + { + return Combine(delegate(IReturnType type) { return type.GetFields(); }); + } + + public override List GetEvents() + { + return Combine(delegate(IReturnType type) { return type.GetEvents(); }); + } + + public override string FullyQualifiedName { + get { + return fullName; + } + } + + public override string Name { + get { + return name; + } + } + + public override string Namespace { + get { + return @namespace; + } + } + + public override string DotNetName { + get { + return dotnetName; + } + } + + public override bool IsDefaultReturnType { + get { + return false; + } + } + + public override int TypeArgumentCount { + get { + return 0; + } + } + + public override IClass GetUnderlyingClass() + { + return null; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CompoundClass.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CompoundClass.cs new file mode 100644 index 000000000..5e8bf8bf0 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CompoundClass.cs @@ -0,0 +1,131 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Diagnostics; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// A class made up of multiple partial classes. + /// + /// CompoundClass is immutable, it freezes the underlying DefaultClass in the constructor. + /// The constructor also freezes all parts to ensure that the methods/properties/fields/events of a + /// CompoundClass never change. + /// When you want to build add or remove parts from a CompoundClass, you need to create a new + /// CompoundClass instance with the new parts. + /// + public sealed class CompoundClass : DefaultClass + { + /// + /// The parts this class is based on. + /// + readonly ReadOnlyCollection parts; + + /// + /// Gets the parts this class is based on. + /// + public ReadOnlyCollection Parts { + get { + return parts; + } + } + + /// + /// Creates a new CompoundClass with the specified parts. + /// + public static CompoundClass Create(IEnumerable parts) + { + // Ensure that the list of parts does not change. + var p = parts.ToList(); + foreach (IClass c in p) { + c.Freeze(); + } + return new CompoundClass(p); + } + + private CompoundClass(List parts) : base(new DefaultCompilationUnit(parts[0].ProjectContent), parts[0].FullyQualifiedName) + { + this.CompilationUnit.Classes.Add(this); + + this.parts = parts.AsReadOnly(); + + UpdateInformationFromParts(); + this.CompilationUnit.Freeze(); + Debug.Assert(this.IsFrozen); + } + + /// + /// Calculate information from class parts (Modifier, Base classes, Type parameters etc.) + /// + void UpdateInformationFromParts() + { + // Common for all parts: + this.ClassType = parts[0].ClassType; + + ModifierEnum modifier = ModifierEnum.None; + const ModifierEnum defaultClassVisibility = ModifierEnum.Internal; + + this.BaseTypes.Clear(); + this.InnerClasses.Clear(); + this.Attributes.Clear(); + this.Methods.Clear(); + this.Properties.Clear(); + this.Events.Clear(); + this.Fields.Clear(); + + string shortestFileName = null; + + foreach (IClass part in parts) { + if (!string.IsNullOrEmpty(part.CompilationUnit.FileName)) { + if (shortestFileName == null || part.CompilationUnit.FileName.Length < shortestFileName.Length) { + shortestFileName = part.CompilationUnit.FileName; + this.Region = part.Region; + } + } + + if ((part.Modifiers & ModifierEnum.VisibilityMask) != defaultClassVisibility) { + modifier |= part.Modifiers; + } else { + modifier |= part.Modifiers &~ ModifierEnum.VisibilityMask; + } + foreach (IReturnType rt in part.BaseTypes) { + if (!rt.IsDefaultReturnType || rt.FullyQualifiedName != "System.Object") { + this.BaseTypes.Add(rt); + } + } + this.InnerClasses.AddRange(part.InnerClasses); + this.Attributes.AddRange(part.Attributes); + this.Methods.AddRange(part.Methods); + this.Properties.AddRange(part.Properties); + this.Events.AddRange(part.Events); + this.Fields.AddRange(part.Fields); + + this.AddDefaultConstructorIfRequired |= part.AddDefaultConstructorIfRequired; + } + this.CompilationUnit.FileName = shortestFileName; + if ((modifier & ModifierEnum.VisibilityMask) == ModifierEnum.None) { + modifier |= defaultClassVisibility; + } + this.Modifiers = modifier; + } + + /// + /// Type parameters are the same on all parts. + /// + public override IList TypeParameters { + get { + // Locking for the time of getting the reference to the sub-list is sufficient: + // Classes used for parts never change, instead the whole part is replaced with + // a new IClass instance. + return parts[0].TypeParameters; + } + set { + throw new NotSupportedException(); + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ConstructedReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ConstructedReturnType.cs new file mode 100644 index 000000000..6231510fe --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ConstructedReturnType.cs @@ -0,0 +1,259 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// ConstructedReturnType is a reference to generic class that specifies the type parameters. + /// When getting the Members, this return type modifies the lists in such a way that the + /// s are replaced with the return types in the type parameters + /// collection. + /// Example: List<string> + /// + public sealed class ConstructedReturnType : DecoratingReturnType + { + // Return types that should be substituted for the generic types + // If a substitution is unknown (type could not be resolved), the list + // contains a null entry. + IList typeArguments; + IReturnType baseType; + + public IList TypeArguments { + get { + return typeArguments; + } + } + + public ConstructedReturnType(IReturnType baseType, IList typeArguments) + { + if (baseType == null) + throw new ArgumentNullException("baseType"); + if (typeArguments == null) + throw new ArgumentNullException("typeArguments"); + this.typeArguments = typeArguments; + this.baseType = baseType; + } + + public override T CastToDecoratingReturnType() + { + if (typeof(T) == typeof(ConstructedReturnType)) { + return (T)(object)this; + } else { + return null; + } + } + + public override bool Equals(IReturnType rt) + { + return rt != null + && rt.IsConstructedReturnType + && this.DotNetName == rt.DotNetName; + } + + public override int GetHashCode() + { + return this.DotNetName.GetHashCode(); + } + + public override IReturnType GetDirectReturnType() + { + IReturnType newBaseType = baseType.GetDirectReturnType(); + IReturnType[] newTypeArguments = new IReturnType[typeArguments.Count]; + bool typeArgumentsChanged = false; + for (int i = 0; i < typeArguments.Count; i++) { + if (typeArguments[i] != null) + newTypeArguments[i] = typeArguments[i].GetDirectReturnType(); + if (typeArguments[i] != newTypeArguments[i]) + typeArgumentsChanged = true; + } + if (baseType == newBaseType && !typeArgumentsChanged) + return this; + else + return new ConstructedReturnType(newBaseType, newTypeArguments); + } + + public override IReturnType BaseType { + get { + return baseType; + } + } + + public IReturnType UnboundType { + get { + return baseType; + } + } + + /// + /// Gets if is/contains a generic return type referring to a class type parameter. + /// + bool CheckReturnType(IReturnType t) + { + if (t == null) { + return false; + } + if (t.IsGenericReturnType) { + return t.CastToGenericReturnType().TypeParameter.Method == null; + } else if (t.IsArrayReturnType) { + return CheckReturnType(t.CastToArrayReturnType().ArrayElementType); + } else if (t.IsConstructedReturnType) { + foreach (IReturnType para in t.CastToConstructedReturnType().TypeArguments) { + if (CheckReturnType(para)) return true; + } + return false; + } else { + return false; + } + } + + bool CheckParameters(IList l) + { + foreach (IParameter p in l) { + if (CheckReturnType(p.ReturnType)) return true; + } + return false; + } + + public override string DotNetName { + get { + string baseName = baseType.DotNetName; + int pos = baseName.LastIndexOf('`'); + StringBuilder b; + if (pos < 0) + b = new StringBuilder(baseName); + else + b = new StringBuilder(baseName, 0, pos, pos + 20); + b.Append('{'); + for (int i = 0; i < typeArguments.Count; ++i) { + if (i > 0) b.Append(','); + if (typeArguments[i] != null) { + b.Append(typeArguments[i].DotNetName); + } + } + b.Append('}'); + return b.ToString(); + } + } + + public static IReturnType TranslateType(IReturnType input, IList typeParameters, bool convertForMethod) + { + if (input == null || typeParameters == null || typeParameters.Count == 0) { + return input; // nothing to do when there are no type parameters specified + } + if (input.IsGenericReturnType) { + GenericReturnType rt = input.CastToGenericReturnType(); + if (convertForMethod ? (rt.TypeParameter.Method != null) : (rt.TypeParameter.Method == null)) { + if (rt.TypeParameter.Index < typeParameters.Count) { + IReturnType newType = typeParameters[rt.TypeParameter.Index]; + if (newType != null) { + return newType; + } + } + } + } else if (input.IsArrayReturnType) { + ArrayReturnType arInput = input.CastToArrayReturnType(); + IReturnType e = arInput.ArrayElementType; + IReturnType t = TranslateType(e, typeParameters, convertForMethod); + if (e != t && t != null) + return new ArrayReturnType(arInput.ProjectContent, t, arInput.ArrayDimensions); + } else if (input.IsConstructedReturnType) { + ConstructedReturnType cinput = input.CastToConstructedReturnType(); + List para = new List(cinput.TypeArguments.Count); + foreach (IReturnType argument in cinput.TypeArguments) { + para.Add(TranslateType(argument, typeParameters, convertForMethod)); + } + return new ConstructedReturnType(cinput.UnboundType, para); + } + return input; + } + + IReturnType TranslateType(IReturnType input) + { + return TranslateType(input, typeArguments, false); + } + + public override List GetMethods() + { + List l = baseType.GetMethods(); + for (int i = 0; i < l.Count; ++i) { + if (CheckReturnType(l[i].ReturnType) || CheckParameters(l[i].Parameters)) { + l[i] = (IMethod)l[i].CreateSpecializedMember(); + if (l[i].DeclaringType == baseType.GetUnderlyingClass()) { + l[i].DeclaringTypeReference = this; + } + l[i].ReturnType = TranslateType(l[i].ReturnType); + for (int j = 0; j < l[i].Parameters.Count; ++j) { + l[i].Parameters[j].ReturnType = TranslateType(l[i].Parameters[j].ReturnType); + } + } + } + return l; + } + + public override List GetProperties() + { + List l = baseType.GetProperties(); + for (int i = 0; i < l.Count; ++i) { + if (CheckReturnType(l[i].ReturnType) || CheckParameters(l[i].Parameters)) { + l[i] = (IProperty)l[i].CreateSpecializedMember(); + if (l[i].DeclaringType == baseType.GetUnderlyingClass()) { + l[i].DeclaringTypeReference = this; + } + l[i].ReturnType = TranslateType(l[i].ReturnType); + for (int j = 0; j < l[i].Parameters.Count; ++j) { + l[i].Parameters[j].ReturnType = TranslateType(l[i].Parameters[j].ReturnType); + } + } + } + return l; + } + + public override List GetFields() + { + List l = baseType.GetFields(); + for (int i = 0; i < l.Count; ++i) { + if (CheckReturnType(l[i].ReturnType)) { + l[i] = (IField)l[i].CreateSpecializedMember(); + if (l[i].DeclaringType == baseType.GetUnderlyingClass()) { + l[i].DeclaringTypeReference = this; + } + l[i].ReturnType = TranslateType(l[i].ReturnType); + } + } + return l; + } + + public override List GetEvents() + { + List l = baseType.GetEvents(); + for (int i = 0; i < l.Count; ++i) { + if (CheckReturnType(l[i].ReturnType)) { + l[i] = (IEvent)l[i].CreateSpecializedMember(); + if (l[i].DeclaringType == baseType.GetUnderlyingClass()) { + l[i].DeclaringTypeReference = this; + } + l[i].ReturnType = TranslateType(l[i].ReturnType); + } + } + return l; + } + + public override string ToString() + { + string r = "[ConstructedReturnType: "; + r += baseType; + r += "<"; + for (int i = 0; i < typeArguments.Count; i++) { + if (i > 0) r += ","; + if (typeArguments[i] != null) { + r += typeArguments[i]; + } + } + return r + ">]"; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DecoratingReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DecoratingReturnType.cs new file mode 100644 index 000000000..77383c64c --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DecoratingReturnType.cs @@ -0,0 +1,29 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// A return type that modifies the base return type and is not regarded equal to its base type. + /// + public abstract class DecoratingReturnType : ProxyReturnType + { + public abstract override bool Equals(IReturnType other); + public abstract override int GetHashCode(); + + public sealed override bool IsDefaultReturnType { + get { + return false; + } + } + + public abstract override T CastToDecoratingReturnType(); + + public override IReturnType GetDirectReturnType() + { + return this; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultAttribute.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultAttribute.cs new file mode 100644 index 000000000..03becb60c --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultAttribute.cs @@ -0,0 +1,88 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class DefaultAttribute : AbstractFreezable, IAttribute + { + public static readonly IList EmptyAttributeList = EmptyList.Instance; + + IList positionalArguments; + IDictionary namedArguments; + + protected override void FreezeInternal() + { + if (positionalArguments.Count == 0) + positionalArguments = EmptyList.Instance; + else + positionalArguments = new ReadOnlyCollection(positionalArguments); + + namedArguments = new ReadOnlyDictionary(namedArguments); + + base.FreezeInternal(); + } + + public DefaultAttribute(IReturnType attributeType) : this(attributeType, AttributeTarget.None) {} + + public DefaultAttribute(IReturnType attributeType, AttributeTarget attributeTarget) + : this(attributeType, attributeTarget, null, null) + { + } + + public DefaultAttribute(IReturnType attributeType, AttributeTarget attributeTarget, IList positionalArguments, IDictionary namedArguments) + { + if (attributeType == null) + throw new ArgumentNullException("attributeType"); + this.AttributeType = attributeType; + this.AttributeTarget = attributeTarget; + this.positionalArguments = positionalArguments ?? new List(); + this.namedArguments = namedArguments ?? new SortedList(); + } + + IReturnType attributeType; + public IReturnType AttributeType { + get { return attributeType; } + set { + CheckBeforeMutation(); + attributeType = value; + } + } + AttributeTarget attributeTarget; + public AttributeTarget AttributeTarget { + get { return attributeTarget; } + set { + CheckBeforeMutation(); + attributeTarget = value; + } + } + + public IList PositionalArguments { + get { return positionalArguments; } + } + + public IDictionary NamedArguments { + get { return namedArguments; } + } + + ICompilationUnit compilationUnit; + public ICompilationUnit CompilationUnit { + get { return compilationUnit; } + set { + CheckBeforeMutation(); + compilationUnit = value; + } + } + DomRegion region; + public DomRegion Region { + get { return region; } + set { + CheckBeforeMutation(); + region = value; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs new file mode 100644 index 000000000..249799c23 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs @@ -0,0 +1,662 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class DefaultClass : AbstractEntity, IClass, IComparable + { + ClassType classType; + DomRegion region; + + ICompilationUnit compilationUnit; + + IList baseTypes; + + IList innerClasses; + IList fields; + IList properties; + IList methods; + IList events; + IList typeParameters; + IUsingScope usingScope; + + protected override void FreezeInternal() + { + baseTypes = FreezeList(baseTypes); + innerClasses = FreezeList(innerClasses); + fields = FreezeList(fields); + properties = FreezeList(properties); + methods = FreezeList(methods); + events = FreezeList(events); + typeParameters = FreezeList(typeParameters); + base.FreezeInternal(); + } + + /* + public virtual IClass Unfreeze() + { + DefaultClass copy = new DefaultClass(compilationUnit, DeclaringType); + copy.FullyQualifiedName = this.FullyQualifiedName; + copy.Attributes.AddRange(this.Attributes); + copy.BaseTypes.AddRange(this.BaseTypes); + copy.BodyRegion = this.BodyRegion; + copy.ClassType = this.ClassType; + copy.Documentation = this.Documentation; + copy.Events.AddRange(this.Events); + copy.Fields.AddRange(this.Fields); + copy.InnerClasses.AddRange(this.InnerClasses); + copy.Methods.AddRange(this.Methods); + copy.Modifiers = this.Modifiers; + copy.Properties.AddRange(this.Properties); + copy.Region = this.Region; + copy.TypeParameters.AddRange(this.TypeParameters); + copy.UserData = this.UserData; + return copy; + } + */ + + byte flags = addDefaultConstructorIfRequiredFlag; + const byte calculatedFlagsReady = 0x01; + const byte hasPublicOrInternalStaticMembersFlag = 0x02; + const byte hasExtensionMethodsFlag = 0x04; + const byte addDefaultConstructorIfRequiredFlag = 0x08; + + internal byte CalculatedFlags { + get { + if ((flags & calculatedFlagsReady) == 0) { + flags |= calculatedFlagsReady; + foreach (IMember m in this.Fields) { + if (m.IsStatic && (m.IsPublic || m.IsInternal)) { + flags |= hasPublicOrInternalStaticMembersFlag; + } + } + foreach (IProperty m in this.Properties) { + if (m.IsStatic && (m.IsPublic || m.IsInternal)) { + flags |= hasPublicOrInternalStaticMembersFlag; + } + if (m.IsExtensionMethod) { + flags |= hasExtensionMethodsFlag; + } + } + foreach (IMethod m in this.Methods) { + if (m.IsStatic && (m.IsPublic || m.IsInternal)) { + flags |= hasPublicOrInternalStaticMembersFlag; + } + if (m.IsExtensionMethod) { + flags |= hasExtensionMethodsFlag; + } + } + foreach (IMember m in this.Events) { + if (m.IsStatic && (m.IsPublic || m.IsInternal)) { + flags |= hasPublicOrInternalStaticMembersFlag; + } + } + foreach (IClass c in this.InnerClasses) { + if (c.IsPublic || c.IsInternal) { + flags |= hasPublicOrInternalStaticMembersFlag; + } + } + } + return flags; + } + set { + CheckBeforeMutation(); + flags = value; + } + } + public bool HasPublicOrInternalStaticMembers { + get { + return (CalculatedFlags & hasPublicOrInternalStaticMembersFlag) == hasPublicOrInternalStaticMembersFlag; + } + } + public bool HasExtensionMethods { + get { + return (CalculatedFlags & hasExtensionMethodsFlag) == hasExtensionMethodsFlag; + } + } + public bool AddDefaultConstructorIfRequired { + get { + return (flags & addDefaultConstructorIfRequiredFlag) == addDefaultConstructorIfRequiredFlag; + } + set { + if (value) + flags |= addDefaultConstructorIfRequiredFlag; + else + flags &= unchecked((byte)~addDefaultConstructorIfRequiredFlag); + } + } + + /// + /// Gets the using scope of contains this class. + /// + public IUsingScope UsingScope { + get { return usingScope; } + set { + if (value == null) + throw new ArgumentNullException("UsingScope"); + CheckBeforeMutation(); + usingScope = value; + } + } + + public DefaultClass(ICompilationUnit compilationUnit, string fullyQualifiedName) : base(null) + { + if (compilationUnit == null) + throw new ArgumentNullException("compilationUnit"); + if (fullyQualifiedName == null) + throw new ArgumentNullException("fullyQualifiedName"); + this.compilationUnit = compilationUnit; + this.FullyQualifiedName = fullyQualifiedName; + this.UsingScope = compilationUnit.UsingScope; + } + + public DefaultClass(ICompilationUnit compilationUnit, IClass declaringType) : base(declaringType) + { + if (compilationUnit == null) + throw new ArgumentNullException("compilationUnit"); + this.compilationUnit = compilationUnit; + this.UsingScope = compilationUnit.UsingScope; + } + + public DefaultClass(ICompilationUnit compilationUnit, ClassType classType, ModifierEnum modifiers, DomRegion region, IClass declaringType) : base(declaringType) + { + if (compilationUnit == null) + throw new ArgumentNullException("compilationUnit"); + this.compilationUnit = compilationUnit; + this.region = region; + this.classType = classType; + Modifiers = modifiers; + this.UsingScope = compilationUnit.UsingScope; + } + + // fields must be volatile to ensure that the optimizer doesn't reorder accesses to it + // or causes DefaultReturnType to return null when the local copy of this.defaultReturnType is + // optimized away. + volatile IReturnType defaultReturnType; + bool hasCompoundClass; + + public IReturnType DefaultReturnType { + get { + IReturnType defaultReturnType = this.defaultReturnType; + if (defaultReturnType == null) { + lock (this) { + this.defaultReturnType = defaultReturnType = CreateDefaultReturnType(); + } + } + return defaultReturnType; + } + } + + protected virtual IReturnType CreateDefaultReturnType() + { + if (hasCompoundClass) { + return new GetClassReturnType(ProjectContent, FullyQualifiedName, TypeParameters.Count); + } else { + return new DefaultReturnType(this); + } + } + + bool IClass.HasCompoundClass { + get { return hasCompoundClass; } + set { + if (hasCompoundClass != value) { + lock (this) { + hasCompoundClass = value; + defaultReturnType = null; + } + } + } + } + + public bool IsPartial { + get { + return (this.Modifiers & ModifierEnum.Partial) == ModifierEnum.Partial; + } + set { + CheckBeforeMutation(); + if (value) + this.Modifiers |= ModifierEnum.Partial; + else + this.Modifiers &= ~ModifierEnum.Partial; + } + } + + public IClass GetCompoundClass() + { + return this.DefaultReturnType.GetUnderlyingClass() ?? this; + } + + protected override void OnFullyQualifiedNameChanged(EventArgs e) + { + base.OnFullyQualifiedNameChanged(e); + defaultReturnType = null; // re-create default return type + } + + public sealed override ICompilationUnit CompilationUnit { + [System.Diagnostics.DebuggerStepThrough] + get { + return compilationUnit; + } + } + + public ClassType ClassType { + get { + return classType; + } + set { + CheckBeforeMutation(); + classType = value; + } + } + + public DomRegion Region { + get { + return region; + } + set { + CheckBeforeMutation(); + region = value; + } + } + + public override string DotNetName { + get { + string fullName; + int typeParametersCount = this.TypeParameters.Count; + if (this.DeclaringType != null) { + fullName = this.DeclaringType.DotNetName + "+" + this.Name; + typeParametersCount -= this.DeclaringType.TypeParameters.Count; + } else { + fullName = this.FullyQualifiedName; + } + if (typeParametersCount == 0) { + return fullName; + } else { + return fullName + "`" + typeParametersCount; + } + } + } + + public override string DocumentationTag { + get { + return "T:" + DotNetName; + } + } + + public IList BaseTypes { + get { + if (baseTypes == null) { + baseTypes = new List(); + } + return baseTypes; + } + } + + public virtual IList InnerClasses { + get { + if (innerClasses == null) { + innerClasses = new List(); + } + return innerClasses; + } + } + + public virtual IList Fields { + get { + if (fields == null) { + fields = new List(); + } + return fields; + } + } + + public virtual IList Properties { + get { + if (properties == null) { + properties = new List(); + } + return properties; + } + } + + public virtual IList Methods { + get { + if (methods == null) { + methods = new List(); + } + return methods; + } + } + + public virtual IList Events { + get { + if (events == null) { + events = new List(); + } + return events; + } + } + + public virtual IList TypeParameters { + get { + if (typeParameters == null) { + typeParameters = new List(); + } + return typeParameters; + } + set { + CheckBeforeMutation(); + typeParameters = value; + } + } + + public virtual int CompareTo(IClass value) + { + int cmp; + + if(0 != (cmp = base.CompareTo((IEntity)value))) { + return cmp; + } + + if (FullyQualifiedName != null) { + cmp = FullyQualifiedName.CompareTo(value.FullyQualifiedName); + if (cmp != 0) { + return cmp; + } + return this.TypeParameters.Count - value.TypeParameters.Count; + } + return -1; + } + + int IComparable.CompareTo(object o) + { + return CompareTo((IClass)o); + } + + volatile IClass[] inheritanceTreeCache; + volatile IClass[] inheritanceTreeClassesOnlyCache; + + public IEnumerable ClassInheritanceTree { + get { + IClass compoundClass = GetCompoundClass(); + if (compoundClass != this) + return compoundClass.ClassInheritanceTree; + + // Notes: + // the ClassInheritanceTree must work even if the following things happen: + // - cyclic inheritance + // - multithreaded calls + + // Recursive calls are possible if the SearchType request done by GetUnderlyingClass() + // uses ClassInheritanceTree. + // Such recursive calls are tricky, they have caused incorrect behavior (SD2-1474) + // or performance problems (SD2-1510) in the past. + // As of revision 3769, NRefactoryAstConvertVisitor sets up the SearchClassReturnType + // used for base types so that it does not look up inner classes in the class itself, + // so the ClassInheritanceTree is not used created in those cases. + // However, other language bindings might not set up base types correctly, so it's + // still possible that ClassInheritanceTree is called recursivly. + // In that case, we'll return an invalid inheritance tree because of + // ProxyReturnType's automatic stack overflow prevention. + + // We do not use locks to protect against multithreaded calls because + // resolving one class's base types can cause getting the inheritance tree + // of another class -> beware of deadlocks + + IClass[] inheritanceTree = this.inheritanceTreeCache; + if (inheritanceTree != null) { + return inheritanceTree; + } + + inheritanceTree = CalculateClassInheritanceTree(false); + + this.inheritanceTreeCache = inheritanceTree; + if (!KeepInheritanceTree) + DomCache.RegisterForClear(ClearCachedInheritanceTree); + + return inheritanceTree; + } + } + + public IEnumerable ClassInheritanceTreeClassesOnly { + get { + IClass compoundClass = GetCompoundClass(); + if (compoundClass != this) + return compoundClass.ClassInheritanceTreeClassesOnly; + + // Notes: + // the ClassInheritanceTree must work even if the following things happen: + // - cyclic inheritance + // - multithreaded calls + + // Recursive calls are possible if the SearchType request done by GetUnderlyingClass() + // uses ClassInheritanceTree. + // Such recursive calls are tricky, they have caused incorrect behavior (SD2-1474) + // or performance problems (SD2-1510) in the past. + // As of revision 3769, NRefactoryAstConvertVisitor sets up the SearchClassReturnType + // used for base types so that it does not look up inner classes in the class itself, + // so the ClassInheritanceTree is not used created in those cases. + // However, other language bindings might not set up base types correctly, so it's + // still possible that ClassInheritanceTree is called recursivly. + // In that case, we'll return an invalid inheritance tree because of + // ProxyReturnType's automatic stack overflow prevention. + + // We do not use locks to protect against multithreaded calls because + // resolving one class's base types can cause getting the inheritance tree + // of another class -> beware of deadlocks + + IClass[] inheritanceTreeClassesOnly = this.inheritanceTreeClassesOnlyCache; + if (inheritanceTreeClassesOnly != null) { + return inheritanceTreeClassesOnly; + } + + inheritanceTreeClassesOnly = CalculateClassInheritanceTree(true); + + this.inheritanceTreeClassesOnlyCache = inheritanceTreeClassesOnly; + if (!KeepInheritanceTree) + DomCache.RegisterForClear(ClearCachedInheritanceTree); + + return inheritanceTreeClassesOnly; + } + } + + void ClearCachedInheritanceTree() + { + inheritanceTreeClassesOnlyCache = null; + inheritanceTreeCache = null; + } + + IClass[] CalculateClassInheritanceTree(bool ignoreInterfaces) + { + List visitedList = new List(); + Queue typesToVisit = new Queue(); + bool enqueuedLastBaseType = false; + IClass currentClass = this; + IReturnType nextType; + do { + if (currentClass != null) { + if ((!ignoreInterfaces || currentClass.ClassType != ClassType.Interface) && !visitedList.Contains(currentClass)) { + visitedList.Add(currentClass); + foreach (IReturnType type in currentClass.BaseTypes) { + typesToVisit.Enqueue(type); + } + } + } + if (typesToVisit.Count > 0) { + nextType = typesToVisit.Dequeue(); + } else { + nextType = enqueuedLastBaseType ? null : GetBaseTypeByClassType(this); + enqueuedLastBaseType = true; + } + if (nextType != null) { + currentClass = nextType.GetUnderlyingClass(); + } + } while (nextType != null); + return visitedList.ToArray(); + } + + /// + /// Specifies whether to keep the inheritance tree when the DomCache is cleared. + /// + protected virtual bool KeepInheritanceTree { + get { return false; } + } + + IReturnType cachedBaseType; + + public IReturnType BaseType { + get { + if (cachedBaseType == null) { + foreach (IReturnType baseType in this.BaseTypes) { + IClass baseClass = baseType.GetUnderlyingClass(); + if (baseClass != null && baseClass.ClassType == this.ClassType) { + cachedBaseType = baseType; + break; + } + } + } + if (cachedBaseType == null) { + return GetBaseTypeByClassType(this); + } else { + return cachedBaseType; + } + } + } + + internal static IReturnType GetBaseTypeByClassType(IClass c) + { + switch (c.ClassType) { + case ClassType.Class: + case ClassType.Interface: + if (c.FullyQualifiedName != "System.Object") { + return c.ProjectContent.SystemTypes.Object; + } + break; + case ClassType.Enum: + return c.ProjectContent.SystemTypes.Enum; + case ClassType.Delegate: + return c.ProjectContent.SystemTypes.Delegate; + case ClassType.Struct: + return c.ProjectContent.SystemTypes.ValueType; + } + return null; + } + + public IClass BaseClass { + get { + foreach (IReturnType baseType in this.BaseTypes) { + IClass baseClass = baseType.GetUnderlyingClass(); + if (baseClass != null && baseClass.ClassType == this.ClassType) + return baseClass; + } + IReturnType defaultBaseType = GetBaseTypeByClassType(this); + if (defaultBaseType != null) + return defaultBaseType.GetUnderlyingClass(); + else + return null; + } + } + + public bool IsTypeInInheritanceTree(IClass possibleBaseClass) + { + if (possibleBaseClass == null) { + return false; + } + foreach (IClass baseClass in this.ClassInheritanceTree) { + if (possibleBaseClass.FullyQualifiedName == baseClass.FullyQualifiedName + && possibleBaseClass.TypeParameters.Count == baseClass.TypeParameters.Count) + return true; + } + return false; + } + + /// + /// Searches the member with the specified name. Returns the first member/overload found. + /// + public IMember SearchMember(string memberName, LanguageProperties language) + { + if (memberName == null || memberName.Length == 0) { + return null; + } + StringComparer cmp = language.NameComparer; + foreach (IProperty p in Properties) { + if (cmp.Equals(p.Name, memberName)) { + return p; + } + } + foreach (IEvent e in Events) { + if (cmp.Equals(e.Name, memberName)) { + return e; + } + } + foreach (IField f in Fields) { + if (cmp.Equals(f.Name, memberName)) { + return f; + } + } + foreach (IMethod m in Methods) { + if (cmp.Equals(m.Name, memberName)) { + return m; + } + } + return null; + } + + public IClass GetInnermostClass(int caretLine, int caretColumn) + { + foreach (IClass c in InnerClasses) { + if (c != null && IsInside(c, caretLine, caretColumn)) { + return c.GetInnermostClass(caretLine, caretColumn); + } + } + return this; + } + + internal static bool IsInside(IClass c, int caretLine, int caretColumn) + { + return c.Region.IsInside(caretLine, caretColumn) + || c.Attributes.Any((IAttribute a) => a.Region.IsInside(caretLine, caretColumn)); + } + + public List GetAccessibleTypes(IClass callingClass) + { + List types = new List(); + List visitedTypes = new List(); + + IClass currentClass = this; + do { + if (visitedTypes.Contains(currentClass)) + break; + visitedTypes.Add(currentClass); + bool isClassInInheritanceTree = callingClass != null ? callingClass.IsTypeInInheritanceTree(currentClass) : false; + foreach (IClass c in currentClass.InnerClasses) { + if (c.IsAccessible(callingClass, isClassInInheritanceTree)) { + types.Add(c); + } + } + currentClass = currentClass.BaseClass; + } while (currentClass != null); + return types; + } + + public IEnumerable AllMembers { + get { + IEnumerable p = properties; + return p.Concat(methods) + .Concat(fields) + .Concat(events); + } + } + + public override EntityType EntityType { + get { + return EntityType.Class; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultComment.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultComment.cs new file mode 100644 index 000000000..bfe333851 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultComment.cs @@ -0,0 +1,47 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom { + + public class DefaultComment : IComment + { + bool isBlockComment; + string commentTag; + string commentText; + DomRegion region; + + public DefaultComment(bool isBlockComment, string commentTag, string commentText, DomRegion region) + { + this.isBlockComment = isBlockComment; + this.commentTag = commentTag; + this.commentText = commentText; + this.region = region; + } + + public bool IsBlockComment { + get { + return isBlockComment; + } + } + + public string CommentTag { + get { + return commentTag; + } + } + + public string CommentText { + get { + return commentText; + } + } + + public DomRegion Region { + get { + return region; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultCompilationUnit.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultCompilationUnit.cs new file mode 100644 index 000000000..9ac253cb4 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultCompilationUnit.cs @@ -0,0 +1,160 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class DefaultCompilationUnit : AbstractFreezable, ICompilationUnit + { + public static readonly ICompilationUnit DummyCompilationUnit = new DefaultCompilationUnit(DefaultProjectContent.DummyProjectContent).FreezeAndReturnSelf(); + + DefaultCompilationUnit FreezeAndReturnSelf() + { + Freeze(); + return this; + } + + IUsingScope usingScope = new DefaultUsingScope(); + IList classes = new List(); + IList attributes = new List(); + IList foldingRegions = new List(); + IList tagComments = new List(); + + protected override void FreezeInternal() + { + // Deep Freeze: freeze lists and their contents + classes = FreezeList(classes); + attributes = FreezeList(attributes); + foldingRegions = FreezeList(foldingRegions); + tagComments = FreezeList(tagComments); + usingScope.Freeze(); + + base.FreezeInternal(); + } + + bool errorsDuringCompile = false; + object tag = null; + string fileName = null; + IProjectContent projectContent; + + /// + /// Source code file this compilation unit was created from. For compiled are compiler-generated + /// code, this property returns null. + /// + public string FileName { + get { + return fileName; + } + set { + CheckBeforeMutation(); + fileName = value; + } + } + + public IProjectContent ProjectContent { + [System.Diagnostics.DebuggerStepThrough] + get { + return projectContent; + } + } + + public bool ErrorsDuringCompile { + get { + return errorsDuringCompile; + } + set { + CheckBeforeMutation(); + errorsDuringCompile = value; + } + } + + public object Tag { + get { + return tag; + } + set { + CheckBeforeMutation(); + tag = value; + } + } + + public virtual IUsingScope UsingScope { + get { return usingScope; } + set { + if (value == null) + throw new ArgumentNullException("UsingScope"); + CheckBeforeMutation(); + usingScope = value; + } + } + + public virtual IList Attributes { + get { + return attributes; + } + } + + public virtual IList Classes { + get { + return classes; + } + } + + public IList FoldingRegions { + get { + return foldingRegions; + } + } + + public virtual IList MiscComments { + get { + return null; + } + } + + public virtual IList DokuComments { + get { + return null; + } + } + + public virtual IList TagComments { + get { + return tagComments; + } + } + + public DefaultCompilationUnit(IProjectContent projectContent) + { + if (projectContent == null) + throw new ArgumentNullException("projectContent"); + this.projectContent = projectContent; + } + + public IClass GetInnermostClass(int caretLine, int caretColumn) + { + foreach (IClass c in Classes) { + if (c != null && DefaultClass.IsInside(c, caretLine, caretColumn)) { + return c.GetInnermostClass(caretLine, caretColumn); + } + } + return null; + } + + public override string ToString() + { + return String.Format("[CompilationUnit: classes = {0}, fileName = {1}]", + classes.Count, + fileName); + } + + public LanguageProperties Language { + get { + return projectContent.Language; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultEvent.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultEvent.cs new file mode 100644 index 000000000..b14c1b2f9 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultEvent.cs @@ -0,0 +1,117 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class DefaultEvent : AbstractMember, IEvent + { + IMethod addMethod; + IMethod removeMethod; + IMethod raiseMethod; + + protected override void FreezeInternal() + { + if (addMethod != null) + addMethod.Freeze(); + if (removeMethod != null) + removeMethod.Freeze(); + if (raiseMethod != null) + raiseMethod.Freeze(); + base.FreezeInternal(); + } + + public override string DocumentationTag { + get { + return "E:" + this.DotNetName; + } + } + + public override IMember Clone() + { + DefaultEvent de = new DefaultEvent(Name, ReturnType, Modifiers, Region, BodyRegion, DeclaringType); + de.CopyDocumentationFrom(this); + foreach (ExplicitInterfaceImplementation eii in InterfaceImplementations) { + de.InterfaceImplementations.Add(eii.Clone()); + } + if (addMethod != null) + de.addMethod = (IMethod)addMethod.Clone(); + if (removeMethod != null) + de.removeMethod = (IMethod)removeMethod.Clone(); + if (raiseMethod != null) + de.raiseMethod = (IMethod)raiseMethod.Clone(); + return de; + } + + public DefaultEvent(IClass declaringType, string name) : base(declaringType, name) + { + } + + public DefaultEvent(string name, IReturnType type, ModifierEnum m, DomRegion region, DomRegion bodyRegion, IClass declaringType) : base(declaringType, name) + { + this.ReturnType = type; + this.Region = region; + this.BodyRegion = bodyRegion; + Modifiers = (ModifierEnum)m; + if (Modifiers == ModifierEnum.None) { + Modifiers = ModifierEnum.Private; + } + } + + public virtual int CompareTo(IEvent value) + { + int cmp; + + if(0 != (cmp = base.CompareTo((IEntity)value))) + return cmp; + + if (FullyQualifiedName != null) { + return FullyQualifiedName.CompareTo(value.FullyQualifiedName); + } + + return 0; + } + + int IComparable.CompareTo(object value) + { + return CompareTo((IEvent)value); + } + + public virtual IMethod AddMethod { + get { + return addMethod; + } + set { + CheckBeforeMutation(); + addMethod = value; + } + } + + public virtual IMethod RemoveMethod { + get { + return removeMethod; + } + set { + CheckBeforeMutation(); + removeMethod = value; + } + } + + public virtual IMethod RaiseMethod { + get { + return raiseMethod; + } + set { + CheckBeforeMutation(); + raiseMethod = value; + } + } + + public override EntityType EntityType { + get { + return EntityType.Event; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultField.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultField.cs new file mode 100644 index 000000000..d71c32b94 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultField.cs @@ -0,0 +1,94 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class DefaultField : AbstractMember, IField + { + public override string DocumentationTag { + get { + return "F:" + this.DotNetName; + } + } + + public DefaultField(IClass declaringType, string name) : base(declaringType, name) + { + } + + public DefaultField(IReturnType type, string name, ModifierEnum m, DomRegion region, IClass declaringType) : base(declaringType, name) + { + this.ReturnType = type; + this.Region = region; + this.Modifiers = m; + } + + public override IMember Clone() + { + DefaultField field = new DefaultField(ReturnType, Name, Modifiers, Region, DeclaringType); + field.CopyDocumentationFrom(this); + return field; + } + + public virtual int CompareTo(IField field) + { + int cmp; + + cmp = base.CompareTo((IEntity)field); + if (cmp != 0) { + return cmp; + } + + if (FullyQualifiedName != null) { + return FullyQualifiedName.CompareTo(field.FullyQualifiedName); + } + return 0; + } + + int IComparable.CompareTo(object value) + { + return CompareTo((IField)value); + } + + /// Gets if this field is a local variable that has been converted into a field. + public virtual bool IsLocalVariable { + get { return false; } + } + + /// Gets if this field is a parameter that has been converted into a field. + public virtual bool IsParameter { + get { return false; } + } + + public override EntityType EntityType { + get { + return EntityType.Field; + } + } + + public class LocalVariableField : DefaultField + { + public override bool IsLocalVariable { + get { return true; } + } + + public LocalVariableField(IReturnType type, string name, DomRegion region, IClass callingClass) + : base(type, name, ModifierEnum.None, region, callingClass) + { + } + } + + public class ParameterField : DefaultField + { + public override bool IsParameter { + get { return true; } + } + + public ParameterField(IReturnType type, string name, DomRegion region, IClass callingClass) + : base(type, name, ModifierEnum.None, region, callingClass) + { + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultMethod.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultMethod.cs new file mode 100644 index 000000000..e7de4fb37 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultMethod.cs @@ -0,0 +1,228 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class Constructor : DefaultMethod + { + public Constructor(ModifierEnum m, DomRegion region, DomRegion bodyRegion, IClass declaringType) + : base((m & ModifierEnum.Static) != 0 ? "#cctor" : "#ctor", + declaringType.DefaultReturnType, + m, region, bodyRegion, declaringType) + { + } + + public Constructor(ModifierEnum m, IReturnType returnType, IClass declaringType) + : base((m & ModifierEnum.Static) != 0 ? "#cctor" : "#ctor", + returnType, m, DomRegion.Empty, DomRegion.Empty, declaringType) + { + } + + /// + /// Creates a default constructor for the class. + /// The constructor has the region of the class and a documentation comment saying + /// it is a default constructor. + /// + public static Constructor CreateDefault(IClass c) + { + if (c == null) + throw new ArgumentNullException("c"); + + ModifierEnum modifiers = ModifierEnum.Synthetic; + if (c.IsAbstract) + modifiers |= ModifierEnum.Protected; + else + modifiers |= ModifierEnum.Public; + DomRegion region = new DomRegion(c.Region.BeginLine, c.Region.BeginColumn, c.Region.BeginLine, c.Region.BeginColumn); + Constructor con = new Constructor(modifiers, region, region, c); + con.Documentation = "Default constructor of " + c.Name; + return con; + } + } + + [Serializable] + public class Destructor : DefaultMethod + { + public Destructor(DomRegion region, DomRegion bodyRegion, IClass declaringType) + : base("#dtor", null, ModifierEnum.None, region, bodyRegion, declaringType) + { + } + } + + [Serializable] + public class DefaultMethod : AbstractMember, IMethod + { + IList parameters; + IList typeParameters; + IList handlesClauses; + + protected override void FreezeInternal() + { + parameters = FreezeList(parameters); + typeParameters = FreezeList(typeParameters); + handlesClauses = FreezeList(handlesClauses); + base.FreezeInternal(); + } + + bool isExtensionMethod; + + public bool IsExtensionMethod { + get { + return isExtensionMethod; + } + set { + CheckBeforeMutation(); + isExtensionMethod = value; + } + } + + public override IMember Clone() + { + DefaultMethod p = new DefaultMethod(Name, ReturnType, Modifiers, Region, BodyRegion, DeclaringType); + p.parameters = DefaultParameter.Clone(this.Parameters); + p.typeParameters = new List(this.typeParameters); + p.CopyDocumentationFrom(this); + p.isExtensionMethod = this.isExtensionMethod; + foreach (ExplicitInterfaceImplementation eii in InterfaceImplementations) { + p.InterfaceImplementations.Add(eii.Clone()); + } + return p; + } + + public override string DotNetName { + get { + if (typeParameters == null || typeParameters.Count == 0) + return base.DotNetName; + else + return base.DotNetName + "``" + typeParameters.Count; + } + } + + public override string DocumentationTag { + get { + string dotnetName = this.DotNetName; + StringBuilder b = new StringBuilder("M:", dotnetName.Length + 2); + b.Append(dotnetName); + IList paras = this.Parameters; + if (paras.Count > 0) { + b.Append('('); + for (int i = 0; i < paras.Count; ++i) { + if (i > 0) b.Append(','); + IReturnType rt = paras[i].ReturnType; + if (rt != null) { + b.Append(rt.DotNetName); + } + } + b.Append(')'); + } + return b.ToString(); + } + } + + public virtual IList TypeParameters { + get { + if (typeParameters == null) { + typeParameters = new List(); + } + return typeParameters; + } + set { + CheckBeforeMutation(); + typeParameters = value; + } + } + + public virtual IList Parameters { + get { + if (parameters == null) { + parameters = new List(); + } + return parameters; + } + set { + CheckBeforeMutation(); + parameters = value; + } + } + + public IList HandlesClauses { + get { + if (handlesClauses == null) { + handlesClauses = new List(); + } + return handlesClauses; + } + set { + CheckBeforeMutation(); + handlesClauses = value; + } + } + + public virtual bool IsConstructor { + get { + return Name == "#ctor" || Name == "#cctor"; + } + } + + public virtual bool IsOperator { + get { + return Name.StartsWith("op_", StringComparison.Ordinal); + } + } + + public DefaultMethod(IClass declaringType, string name) : base(declaringType, name) + { + } + + public DefaultMethod(string name, IReturnType type, ModifierEnum m, DomRegion region, DomRegion bodyRegion, IClass declaringType) : base(declaringType, name) + { + this.ReturnType = type; + this.Region = region; + this.BodyRegion = bodyRegion; + Modifiers = m; + } + + public override string ToString() + { + return String.Format("[DefaultMethod: {0}]", + (new Dom.CSharp.CSharpAmbience { + ConversionFlags = ConversionFlags.StandardConversionFlags + | ConversionFlags.UseFullyQualifiedMemberNames + }).Convert(this)); + } + + public virtual int CompareTo(IMethod value) + { + int cmp = string.CompareOrdinal(this.FullyQualifiedName, value.FullyQualifiedName); + if (cmp != 0) { + return cmp; + } + + cmp = this.TypeParameters.Count - value.TypeParameters.Count; + if (cmp != 0) { + return cmp; + } + + return DiffUtility.Compare(Parameters, value.Parameters); + } + + int IComparable.CompareTo(object value) + { + if (value == null) { + return 0; + } + return CompareTo((IMethod)value); + } + + public override EntityType EntityType { + get { + return EntityType.Method; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultParameter.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultParameter.cs new file mode 100644 index 000000000..8acf8f2da --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultParameter.cs @@ -0,0 +1,173 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class DefaultParameter : AbstractFreezable, IParameter + { + public static readonly IList EmptyParameterList = EmptyList.Instance; + + string name; + string documentation; + +// int nameHashCode = -1; +// int documentationHash = -1; + + IReturnType returnType; + ParameterModifiers modifier; + DomRegion region; + IList attributes; + + protected override void FreezeInternal() + { + attributes = FreezeList(attributes); + base.FreezeInternal(); + } + + protected DefaultParameter(string name) + { + Name = name; + } + + public DefaultParameter(IParameter p) + { + this.name = p.Name; + this.region = p.Region; + this.modifier = p.Modifiers; + this.returnType = p.ReturnType; + } + + public DefaultParameter(string name, IReturnType type, DomRegion region) : this(name) + { + returnType = type; + this.region = region; + } + + public DomRegion Region { + get { + return region; + } + } + public bool IsOut { + get { + return (modifier & ParameterModifiers.Out) == ParameterModifiers.Out; + } + } + public bool IsRef { + get { + return (modifier & ParameterModifiers.Ref) == ParameterModifiers.Ref; + } + } + public bool IsParams { + get { + return (modifier & ParameterModifiers.Params) == ParameterModifiers.Params; + } + } + public bool IsOptional { + get { + return (modifier & ParameterModifiers.Optional) == ParameterModifiers.Optional; + } + } + + public virtual string Name { + get { + return name; +// return (string)AbstractNamedEntity.fullyQualifiedNames[nameHashCode]; + } + set { + CheckBeforeMutation(); + name = value; +// nameHashCode = value.GetHashCode(); +// if (AbstractNamedEntity.fullyQualifiedNames[nameHashCode] == null) { +// AbstractNamedEntity.fullyQualifiedNames[nameHashCode] = value; +// } + } + } + + public virtual IReturnType ReturnType { + get { + return returnType; + } + set { + CheckBeforeMutation(); + returnType = value; + } + } + + public virtual IList Attributes { + get { + if (attributes == null) { + attributes = new List(); + } + return attributes; + } + set { + CheckBeforeMutation(); + attributes = value; + } + } + + public virtual ParameterModifiers Modifiers { + get { + return modifier; + } + set { + CheckBeforeMutation(); + modifier = value; + } + } + + public string Documentation { + get { + return documentation; +// if (documentationHash == -1) { +// return String.Empty; +// } +// return (string)AbstractDecoration.documentationHashtable[documentationHash]; + } + set { + CheckBeforeMutation(); + documentation = value; +// documentationHash = value.GetHashCode(); +// if (AbstractDecoration.documentationHashtable[documentationHash] == null) { +// AbstractDecoration.documentationHashtable[documentationHash] = value; +// } + } + } + + public static List Clone(IList l) + { + List r = new List(l.Count); + for (int i = 0; i < l.Count; ++i) { + r.Add(new DefaultParameter(l[i])); + } + return r; + } + + public virtual int CompareTo(IParameter value) + { + if (value == null) return -1; + + // two parameters are equal if they have the same return type + // (they may have different names) + if (object.Equals(ReturnType, value.ReturnType)) { + return 0; + } else { + // if the parameters are not equal, use the parameter name to provide the ordering + int r = string.Compare(this.Name, value.Name); + if (r != 0) + return r; + else + return -1; // but equal names don't make parameters of different return types equal + } + } + + int IComparable.CompareTo(object value) + { + return CompareTo(value as IParameter); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultProperty.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultProperty.cs new file mode 100644 index 000000000..557a8bc81 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultProperty.cs @@ -0,0 +1,175 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.SharpDevelop.Dom { + + public class DefaultProperty : AbstractMember, IProperty + { + DomRegion getterRegion = DomRegion.Empty; + DomRegion setterRegion = DomRegion.Empty; + + IList parameters = null; + internal byte accessFlags; + const byte indexerFlag = 0x01; + const byte getterFlag = 0x02; + const byte setterFlag = 0x04; + const byte extensionFlag = 0x08; + ModifierEnum getterModifiers, setterModifiers; + + protected override void FreezeInternal() + { + parameters = FreezeList(parameters); + base.FreezeInternal(); + } + + public bool IsIndexer { + get { return (accessFlags & indexerFlag) == indexerFlag; } + set { + CheckBeforeMutation(); + if (value) accessFlags |= indexerFlag; else accessFlags &= 255-indexerFlag; + } + } + + public bool CanGet { + get { return (accessFlags & getterFlag) == getterFlag; } + set { + CheckBeforeMutation(); + if (value) accessFlags |= getterFlag; else accessFlags &= 255-getterFlag; + } + } + + public bool CanSet { + get { return (accessFlags & setterFlag) == setterFlag; } + set { + CheckBeforeMutation(); + if (value) accessFlags |= setterFlag; else accessFlags &= 255-setterFlag; + } + } + + public bool IsExtensionMethod { + get { return (accessFlags & extensionFlag) == extensionFlag; } + set { + CheckBeforeMutation(); + if (value) accessFlags |= extensionFlag; else accessFlags &= 255-extensionFlag; + } + } + + public override string DocumentationTag { + get { + string dotnetName = this.DotNetName; + StringBuilder b = new StringBuilder("P:", dotnetName.Length + 2); + b.Append(dotnetName); + IList paras = this.Parameters; + if (paras.Count > 0) { + b.Append('('); + for (int i = 0; i < paras.Count; ++i) { + if (i > 0) b.Append(','); + IReturnType rt = paras[i].ReturnType; + if (rt != null) { + b.Append(rt.DotNetName); + } + } + b.Append(')'); + } + return b.ToString(); + } + } + + public override IMember Clone() + { + DefaultProperty p = new DefaultProperty(Name, ReturnType, Modifiers, Region, BodyRegion, DeclaringType); + p.parameters = DefaultParameter.Clone(this.Parameters); + p.getterModifiers = this.getterModifiers; + p.setterModifiers = this.setterModifiers; + p.getterRegion = this.getterRegion; + p.setterRegion = this.setterRegion; + p.CopyDocumentationFrom(this); + p.accessFlags = this.accessFlags; + foreach (ExplicitInterfaceImplementation eii in InterfaceImplementations) { + p.InterfaceImplementations.Add(eii.Clone()); + } + return p; + } + + public virtual IList Parameters { + get { + if (parameters == null) { + parameters = new List(); + } + return parameters; + } + set { + CheckBeforeMutation(); + parameters = value; + } + } + + public DomRegion GetterRegion { + get { return getterRegion; } + set { + CheckBeforeMutation(); + getterRegion = value; + } + } + + public DomRegion SetterRegion { + get { return setterRegion; } + set { + CheckBeforeMutation(); + setterRegion = value; + } + } + + public ModifierEnum GetterModifiers { + get { return getterModifiers; } + set { + CheckBeforeMutation(); + getterModifiers = value; + } + } + + public ModifierEnum SetterModifiers { + get { return setterModifiers; } + set { + CheckBeforeMutation(); + setterModifiers = value; + } + } + + public DefaultProperty(IClass declaringType, string name) : base(declaringType, name) + { + } + + public DefaultProperty(string name, IReturnType type, ModifierEnum m, DomRegion region, DomRegion bodyRegion, IClass declaringType) : base(declaringType, name) + { + this.ReturnType = type; + this.Region = region; + this.BodyRegion = bodyRegion; + Modifiers = m; + } + + public virtual int CompareTo(IProperty value) + { + int cmp = string.CompareOrdinal(this.FullyQualifiedName, value.FullyQualifiedName); + if (cmp != 0) { + return cmp; + } + + return DiffUtility.Compare(Parameters, value.Parameters); + } + + int IComparable.CompareTo(object value) { + return CompareTo((IProperty)value); + } + + public override EntityType EntityType { + get { + return EntityType.Property; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultReturnType.cs new file mode 100644 index 000000000..ce7327b1f --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultReturnType.cs @@ -0,0 +1,254 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// DefaultReturnType is a reference to a normal class or a reference to a generic class where + /// the type parameters are NOT specified. + /// E.g. "System.Int32", "System.Void", "System.String", "System.Collections.Generic.List" + /// + public class DefaultReturnType : AbstractReturnType + { + public static bool Equals(IReturnType rt1, IReturnType rt2) + { + if (rt1 == rt2) return true; + if (rt1 == null || rt2 == null) return false; + IClass c1 = rt1.GetUnderlyingClass(); + IClass c2 = rt2.GetUnderlyingClass(); + if (c1 == null && c2 == null) { + // guess if the classes are equal + return rt1.FullyQualifiedName == rt2.FullyQualifiedName && rt1.TypeArgumentCount == rt2.TypeArgumentCount; + } else { + if (c1 == c2) + return true; + if (c1 == null || c2 == null) + return false; + return c1.FullyQualifiedName == c2.FullyQualifiedName && c1.TypeParameters.Count == c2.TypeParameters.Count; + } + } + + public static int GetHashCode(IReturnType rt) + { + if (rt == null) + return 0; + return (rt.FullyQualifiedName ?? "").GetHashCode() ^ rt.TypeArgumentCount; + } + + IClass c; + + public DefaultReturnType(IClass c) + { + if (c == null) + throw new ArgumentNullException("c"); + this.c = c; + } + + public override string ToString() + { + return c.FullyQualifiedName; + } + + public override int TypeArgumentCount { + get { + return c.TypeParameters.Count; + } + } + + public override IClass GetUnderlyingClass() + { + return c; + } + + // Required to prevent stack overflow when calling GetMethods() on a class with cyclic inheritance + // replaces old 'getMembersBusy' flag which wasn't thread-safe + [ThreadStatic] static BusyManager _busyManager; + + static BusyManager busyManager { + get { return _busyManager ?? (_busyManager = new BusyManager()); } + } + + public override List GetMethods() + { + List l = new List(); + using (var busyLock = busyManager.Enter(this)) { + if (busyLock.Success) { + l.AddRange(c.Methods); + if (c.AddDefaultConstructorIfRequired && !c.IsStatic) { + // A constructor is added for classes that do not have a default constructor; + // and for all structs. + if (c.ClassType == ClassType.Class && !l.Exists(m => m.IsConstructor)) { + l.Add(Constructor.CreateDefault(c)); + } else if (c.ClassType == ClassType.Struct || c.ClassType == ClassType.Enum) { + l.Add(Constructor.CreateDefault(c)); + } + } + + if (c.ClassType == ClassType.Interface) { + if (c.BaseTypes.Count == 0) { + AddMethodsFromBaseType(l, c.ProjectContent.SystemTypes.Object); + } else { + foreach (IReturnType baseType in c.BaseTypes) { + AddMethodsFromBaseType(l, baseType); + } + } + } else { + AddMethodsFromBaseType(l, c.BaseType); + } + } + } + return l; + } + + void AddMethodsFromBaseType(List l, IReturnType baseType) + { + if (baseType != null) { + foreach (IMethod m in baseType.GetMethods()) { + if (m.IsConstructor) + continue; + + /*bool ok = true; + if (m.IsOverridable) { + StringComparer comparer = m.DeclaringType.ProjectContent.Language.NameComparer; + foreach (IMethod oldMethod in c.Methods) { + if (comparer.Equals(oldMethod.Name, m.Name)) { + if (m.IsStatic == oldMethod.IsStatic && object.Equals(m.ReturnType, oldMethod.ReturnType)) { + if (DiffUtility.Compare(oldMethod.Parameters, m.Parameters) == 0) { + ok = false; + break; + } + } + } + } + } + if (ok) + l.Add(m);*/ + l.Add(m); + } + } + } + + public override List GetProperties() + { + List l = new List(); + using (var busyLock = busyManager.Enter(this)) { + if (busyLock.Success) { + l.AddRange(c.Properties); + if (c.ClassType == ClassType.Interface) { + foreach (IReturnType baseType in c.BaseTypes) { + AddPropertiesFromBaseType(l, baseType); + } + } else { + AddPropertiesFromBaseType(l, c.BaseType); + } + } + } + return l; + } + + void AddPropertiesFromBaseType(List l, IReturnType baseType) + { + if (baseType != null) { + foreach (IProperty p in baseType.GetProperties()) { + /*bool ok = true; + if (p.IsOverridable) { + StringComparer comparer = p.DeclaringType.ProjectContent.Language.NameComparer; + foreach (IProperty oldProperty in c.Properties) { + if (comparer.Equals(oldProperty.Name, p.Name)) { + if (p.IsStatic == oldProperty.IsStatic && object.Equals(p.ReturnType, oldProperty.ReturnType)) { + if (DiffUtility.Compare(oldProperty.Parameters, p.Parameters) == 0) { + ok = false; + break; + } + } + } + } + } + if (ok) + l.Add(p);*/ + l.Add(p); + } + } + } + + public override List GetFields() + { + List l = new List(); + using (var busyLock = busyManager.Enter(this)) { + if (busyLock.Success) { + l.AddRange(c.Fields); + if (c.ClassType == ClassType.Interface) { + foreach (IReturnType baseType in c.BaseTypes) { + l.AddRange(baseType.GetFields()); + } + } else { + IReturnType baseType = c.BaseType; + if (baseType != null) { + l.AddRange(baseType.GetFields()); + } + } + } + } + return l; + } + + public override List GetEvents() + { + List l = new List(); + using (var busyLock = busyManager.Enter(this)) { + if (busyLock.Success) { + l.AddRange(c.Events); + if (c.ClassType == ClassType.Interface) { + foreach (IReturnType baseType in c.BaseTypes) { + l.AddRange(baseType.GetEvents()); + } + } else { + IReturnType baseType = c.BaseType; + if (baseType != null) { + l.AddRange(baseType.GetEvents()); + } + } + } + } + return l; + } + + public override string FullyQualifiedName { + get { + return c.FullyQualifiedName; + } + set { + throw new NotSupportedException(); + } + } + + public override string Name { + get { + return c.Name; + } + } + + public override string Namespace { + get { + return c.Namespace; + } + } + + public override string DotNetName { + get { + return c.DotNetName; + } + } + + public override Nullable IsReferenceType { + get { + return (this.c.ClassType == ClassType.Class + || this.c.ClassType == ClassType.Interface + || this.c.ClassType == ClassType.Delegate); + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultTypeParameter.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultTypeParameter.cs new file mode 100644 index 000000000..95561e487 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultTypeParameter.cs @@ -0,0 +1,194 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Type parameter of a generic class/method. + /// + public class DefaultTypeParameter : AbstractFreezable, ITypeParameter + { + public static readonly IList EmptyTypeParameterList = EmptyList.Instance; + + readonly string name; + readonly IMethod method; + readonly IClass targetClass; + readonly int index; + IList constraints = new List(); + + protected override void FreezeInternal() + { + constraints = FreezeList(constraints); + base.FreezeInternal(); + } + + public string Name { + get { + return name; + } + } + + public int Index { + get { + return index; + } + } + + public IMethod Method { + get { + return method; + } + } + + public IClass Class { + get { + return targetClass; + } + } + + public IList Constraints { + get { + return constraints; + } + } + + public IList Attributes { + get { + return DefaultAttribute.EmptyAttributeList; + } + } + + bool hasConstructableConstraint = false; + bool hasReferenceTypeConstraint = false; + bool hasValueTypeConstraint = false; + + /// + /// Gets/Sets if the type parameter has the 'new()' constraint. + /// + public bool HasConstructableConstraint { + get { return hasConstructableConstraint; } + set { + CheckBeforeMutation(); + hasConstructableConstraint = value; + } + } + + /// + /// Gets/Sets if the type parameter has the 'class' constraint. + /// + public bool HasReferenceTypeConstraint { + get { return hasReferenceTypeConstraint; } + set { + CheckBeforeMutation(); + hasReferenceTypeConstraint = value; + } + } + + /// + /// Gets/Sets if the type parameter has the 'struct' constraint. + /// + public bool HasValueTypeConstraint { + get { return hasValueTypeConstraint; } + set { + CheckBeforeMutation(); + hasValueTypeConstraint = value; + } + } + + public DefaultTypeParameter(IMethod method, string name, int index) + { + this.method = method; + this.targetClass = method.DeclaringType; + this.name = name; + this.index = index; + } + + public DefaultTypeParameter(IMethod method, Type type) + { + this.method = method; + this.targetClass = method.DeclaringType; + this.name = type.Name; + this.index = type.GenericParameterPosition; + } + + public DefaultTypeParameter(IClass targetClass, string name, int index) + { + this.targetClass = targetClass; + this.name = name; + this.index = index; + } + + public DefaultTypeParameter(IClass targetClass, Type type) + { + this.targetClass = targetClass; + this.name = type.Name; + this.index = type.GenericParameterPosition; + } + + public override bool Equals(object obj) + { + DefaultTypeParameter tp = obj as DefaultTypeParameter; + if (tp == null) return false; + if (tp.index != index) return false; + if (tp.name != name) return false; + if (tp.hasConstructableConstraint != hasConstructableConstraint) return false; + if (tp.hasReferenceTypeConstraint != hasReferenceTypeConstraint) return false; + if (tp.hasValueTypeConstraint != hasValueTypeConstraint) return false; + if (tp.method != method) { + if (tp.method == null || method == null) return false; + if (tp.method.FullyQualifiedName != method.FullyQualifiedName) return false; + } else { + if (tp.targetClass.FullyQualifiedName != targetClass.FullyQualifiedName) return false; + } + return true; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override string ToString() + { + return String.Format("[{0}: {1}]", GetType().Name, name); + } + + public static DefaultClass GetDummyClassForTypeParameter(ITypeParameter p) + { + DefaultClass c = new DefaultClass(p.Class.CompilationUnit, p.Name); + if (p.Method != null) { + c.Region = new DomRegion(p.Method.Region.BeginLine, p.Method.Region.BeginColumn); + } else { + c.Region = new DomRegion(p.Class.Region.BeginLine, p.Class.Region.BeginColumn); + } + c.Modifiers = ModifierEnum.Public; + if (p.HasValueTypeConstraint) { + c.ClassType = ClassType.Struct; + } else if (p.HasConstructableConstraint) { + c.ClassType = ClassType.Class; + } else { + c.ClassType = ClassType.Interface; + } + return c; + } + + /// + /// Gets the type that was used to bind this type parameter. + /// This property returns null for generic methods/classes, it + /// is non-null only for constructed versions of generic methods. + /// + public virtual IReturnType BoundTo { + get { return null; } + } + + /// + /// If this type parameter was bound, returns the unbound version of it. + /// + public virtual ITypeParameter UnboundTypeParameter { + get { return this; } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultUsing.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultUsing.cs new file mode 100644 index 000000000..85e2c6ad4 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultUsing.cs @@ -0,0 +1,174 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class DefaultUsing : AbstractFreezable, IUsing + { + DomRegion region; + IProjectContent projectContent; + + public DefaultUsing(IProjectContent projectContent) + { + this.projectContent = projectContent; + } + + public DefaultUsing(IProjectContent projectContent, DomRegion region) : this(projectContent) + { + this.region = region; + } + + IList usings = new List(); + IDictionary aliases = null; + + protected override void FreezeInternal() + { + usings = FreezeList(usings); + if (aliases != null) + aliases = new ReadOnlyDictionary(aliases); + base.FreezeInternal(); + } + + public DomRegion Region { + get { + return region; + } + } + + public IList Usings { + get { + return usings; + } + } + + public IDictionary Aliases { + get { + return aliases; + } + } + + public bool HasAliases { + get { + return aliases != null && aliases.Count > 0; + } + } + + public void AddAlias(string alias, IReturnType type) + { + CheckBeforeMutation(); + if (aliases == null) aliases = new SortedList(); + aliases.Add(alias, type); + } + + public string SearchNamespace(string partialNamespaceName) + { + if (HasAliases) { + foreach (KeyValuePair entry in aliases) { + string aliasString = entry.Key; + string nsName; + if (projectContent.Language.NameComparer.Equals(partialNamespaceName, aliasString)) { + nsName = entry.Value.FullyQualifiedName; + if (projectContent.NamespaceExists(nsName)) + return nsName; + } + if (partialNamespaceName.Length > aliasString.Length) { + if (projectContent.Language.NameComparer.Equals(partialNamespaceName.Substring(0, aliasString.Length + 1), aliasString + ".")) { + nsName = String.Concat(entry.Value.FullyQualifiedName, partialNamespaceName.Remove(0, aliasString.Length)); + if (projectContent.NamespaceExists(nsName)) { + return nsName; + } + } + } + } + } + if (projectContent.Language.ImportNamespaces) { + foreach (string str in usings) { + string possibleNamespace = String.Concat(str, ".", partialNamespaceName); + if (projectContent.NamespaceExists(possibleNamespace)) + return possibleNamespace; + } + } + return null; + } + + /// + /// Returns a collection of possible types that could be meant when using this Import + /// to search the type. + /// Types with the incorrect type parameter count might be returned, but for each + /// same using entry or alias entry at most one (the best matching) type should be returned. + /// + public IEnumerable SearchType(string partialTypeName, int typeParameterCount) + { + if (HasAliases) { + foreach (KeyValuePair entry in aliases) { + string aliasString = entry.Key; + if (projectContent.Language.NameComparer.Equals(partialTypeName, aliasString)) { + if (entry.Value.GetUnderlyingClass() == null) + continue; // type not found, maybe entry was a namespace + yield return entry.Value; + } + if (partialTypeName.Length > aliasString.Length) { + if (projectContent.Language.NameComparer.Equals(partialTypeName.Substring(0, aliasString.Length + 1), aliasString + ".")) { + string className = entry.Value.FullyQualifiedName + partialTypeName.Remove(0, aliasString.Length); + IClass c = projectContent.GetClass(className, typeParameterCount); + if (c != null) { + yield return c.DefaultReturnType; + } + } + } + } + } + if (projectContent.Language.ImportNamespaces) { + foreach (string str in usings) { + IClass c = projectContent.GetClass(str + "." + partialTypeName, typeParameterCount); + if (c != null) { + yield return c.DefaultReturnType; + } + } + } else { + int pos = partialTypeName.IndexOf('.'); + string className, subClassName; + if (pos < 0) { + className = partialTypeName; + subClassName = null; + } else { + className = partialTypeName.Substring(0, pos); + subClassName = partialTypeName.Substring(pos + 1); + } + foreach (string str in usings) { + IClass c = projectContent.GetClass(str + "." + className, typeParameterCount); + if (c != null) { + c = projectContent.GetClass(str + "." + partialTypeName, typeParameterCount); + if (c != null) { + yield return c.DefaultReturnType; + } + } + } + } + } + + public override string ToString() + { + StringBuilder builder = new StringBuilder("[DefaultUsing: "); + foreach (string str in usings) { + builder.Append(str); + builder.Append(", "); + } + if (HasAliases) { + foreach (KeyValuePair p in aliases) { + builder.Append(p.Key); + builder.Append("="); + builder.Append(p.Value.ToString()); + builder.Append(", "); + } + } + builder.Length -= 2; // remove last ", " + builder.Append("]"); + return builder.ToString(); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultUsingScope.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultUsingScope.cs new file mode 100644 index 000000000..ea9088d7c --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultUsingScope.cs @@ -0,0 +1,66 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class DefaultUsingScope : AbstractFreezable, IUsingScope + { + DomRegion region; + IUsingScope parent; + IList usings; + IList childScopes; + string namespaceName = ""; + + protected override void FreezeInternal() + { + base.FreezeInternal(); + usings = FreezeList(usings); + childScopes = FreezeList(childScopes); + } + + public DomRegion Region { + get { return region; } + set { + CheckBeforeMutation(); + region = value; + } + } + + public IUsingScope Parent { + get { return parent; } + set { + CheckBeforeMutation(); + parent = value; + } + } + + public virtual IList Usings { + get { + if (usings == null) + usings = new List(); + return usings; + } + } + + public virtual IList ChildScopes { + get { + if (childScopes == null) + childScopes = new List(); + return childScopes; + } + } + + public string NamespaceName { + get { return namespaceName; } + set { + if (value == null) + throw new ArgumentNullException("NamespaceName"); + CheckBeforeMutation(); + namespaceName = value; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DynamicReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DynamicReturnType.cs new file mode 100644 index 000000000..d1f199474 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DynamicReturnType.cs @@ -0,0 +1,51 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class DynamicReturnType : AbstractReturnType + { + readonly IProjectContent pc; + + public DynamicReturnType(IProjectContent pc) + { + if (pc == null) + throw new ArgumentNullException("pc"); + this.pc = pc; + } + + public override IClass GetUnderlyingClass() + { + return null; + } + + public override List GetMethods() + { + return new List(); + } + public override List GetProperties() + { + return new List(); + } + public override List GetFields() + { + return new List(); + } + public override List GetEvents() + { + return new List(); + } + + public override string Name { + get { return "dynamic"; } + } + + public override string FullyQualifiedName { + get { return "dynamic"; } + set { throw new NotSupportedException(); } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ElementReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ElementReturnType.cs new file mode 100644 index 000000000..3b3de5351 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ElementReturnType.cs @@ -0,0 +1,64 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// The type reference that is the element of an enumerable. + /// This class is used in combination with an InferredReturnType to + /// represent the implicitly typed loop variable v in + /// "foreach (var v in enumerableInstance) {}" + /// + public class ElementReturnType : ProxyReturnType + { + IReturnType enumerableType; + IProjectContent pc; + + public ElementReturnType(IProjectContent pc, IReturnType enumerableType) + { + if (pc == null) + throw new ArgumentNullException("pc"); + this.enumerableType = enumerableType; + this.pc = pc; + } + + [ThreadStatic] static BusyManager _busyManager; + + static BusyManager busyManager { + get { return _busyManager ?? (_busyManager = new BusyManager()); } + } + + public override IReturnType BaseType { + get { + using (var l = busyManager.Enter(this)) { + if (!l.Success) + return null; + + // get element type from enumerableType + if (enumerableType.IsArrayReturnType) + return enumerableType.CastToArrayReturnType().ArrayElementType; + + IClass c = enumerableType.GetUnderlyingClass(); + if (c == null) + return null; + IClass genumerable = pc.GetClass("System.Collections.Generic.IEnumerable", 1); + if (c.IsTypeInInheritanceTree(genumerable)) { + return MemberLookupHelper.GetTypeParameterPassedToBaseClass(enumerableType, genumerable, 0); + } + IClass enumerable = pc.GetClass("System.Collections.IEnumerable", 0); + if (c.IsTypeInInheritanceTree(enumerable)) { + return pc.SystemTypes.Object; + } + return null; + } + } + } + + public override string ToString() + { + return "[ElementReturnType " + enumerableType + "]"; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/GenericReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/GenericReturnType.cs new file mode 100644 index 000000000..73f8283b8 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/GenericReturnType.cs @@ -0,0 +1,125 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// GenericReturnType is a reference to a type parameter. + /// + public sealed class GenericReturnType : DecoratingReturnType + { + ITypeParameter typeParameter; + + public ITypeParameter TypeParameter { + get { + return typeParameter; + } + } + + public override bool Equals(IReturnType rt) + { + if (rt == null || !rt.IsGenericReturnType) + return false; + GenericReturnType grt = rt.CastToGenericReturnType(); + if ((typeParameter.Method == null) != (grt.typeParameter.Method == null)) + return false; + return typeParameter.Index == grt.typeParameter.Index; + } + + public override int GetHashCode() + { + if (typeParameter.Method != null) + return 17491 + typeParameter.Index; + else + return 81871 + typeParameter.Index; + } + + public override T CastToDecoratingReturnType() + { + if (typeof(T) == typeof(GenericReturnType)) { + return (T)(object)this; + } else { + return null; + } + } + + public GenericReturnType(ITypeParameter typeParameter) + { + if (typeParameter == null) + throw new ArgumentNullException("typeParameter"); + this.typeParameter = typeParameter; + } + + public override string FullyQualifiedName { + get { + return typeParameter.Name; + } + } + + public override string Name { + get { + return typeParameter.Name; + } + } + + public override string Namespace { + get { + return ""; + } + } + + public override string DotNetName { + get { + if (typeParameter.Method != null) + return "``" + typeParameter.Index; + else + return "`" + typeParameter.Index; + } + } + + public override IClass GetUnderlyingClass() + { + return null; + } + + public override IReturnType BaseType { + get { + int count = typeParameter.Constraints.Count; + if (count == 0) + return typeParameter.Class.ProjectContent.SystemTypes.Object; + if (count == 1) + return typeParameter.Constraints[0]; + return new CombinedReturnType(typeParameter.Constraints, + FullyQualifiedName, + Name, Namespace, + DotNetName); + } + } + + // remove static methods (T.ReferenceEquals() is not possible) + public override List GetMethods() + { + List list = base.GetMethods(); + if (list != null) { + list.RemoveAll(delegate(IMethod m) { return m.IsStatic || m.IsConstructor; }); + if (typeParameter.HasConstructableConstraint || typeParameter.HasValueTypeConstraint) { + list.Add(new Constructor(ModifierEnum.Public, this, + DefaultTypeParameter.GetDummyClassForTypeParameter(typeParameter))); + } + } + return list; + } + + public override Nullable IsReferenceType { + get { return null; } + } + + public override string ToString() + { + return String.Format("[GenericReturnType: {0}]", typeParameter); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/GetClassReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/GetClassReturnType.cs new file mode 100644 index 000000000..49cd8665c --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/GetClassReturnType.cs @@ -0,0 +1,104 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// The GetClassReturnType is used when the class should be resolved on demand, but the + /// full name is already known. + /// + public sealed class GetClassReturnType : ProxyReturnType + { + IProjectContent content; + string fullName; + string shortName; + int typeArgumentCount; + GetClassOptions options; + + public GetClassReturnType(IProjectContent content, string fullName, int typeArgumentCount) + : this(content, fullName, typeArgumentCount, GetClassOptions.Default) + { + } + + public GetClassReturnType(IProjectContent content, string fullName, int typeArgumentCount, GetClassOptions options) + { + this.content = content; + this.typeArgumentCount = typeArgumentCount; + SetFullyQualifiedName(fullName); + this.options = options; + } + + public override bool IsDefaultReturnType { + get { + return true; + } + } + + public override int TypeArgumentCount { + get { + return typeArgumentCount; + } + } + + public override IReturnType BaseType { + get { + IClass c = content.GetClass(fullName, typeArgumentCount, content.Language, options); + return (c != null) ? c.DefaultReturnType : null; + } + } + + public override string FullyQualifiedName { + get { + return fullName; + } + } + + void SetFullyQualifiedName(string fullName) + { + if (fullName == null) + throw new ArgumentNullException("fullName"); + this.fullName = fullName; + int pos = fullName.LastIndexOf('.'); + if (pos < 0) + shortName = fullName; + else + shortName = fullName.Substring(pos + 1); + } + + public override string Name { + get { + return shortName; + } + } + + public override string Namespace { + get { + string tmp = base.Namespace; + if (tmp == "?") { + if (fullName.IndexOf('.') > 0) + return fullName.Substring(0, fullName.LastIndexOf('.')); + else + return ""; + } + return tmp; + } + } + + public override string DotNetName { + get { + string tmp = base.DotNetName; + if (tmp == "?") { + return fullName; + } + return tmp; + } + } + + public override string ToString() + { + return String.Format("[GetClassReturnType: {0}]", fullName); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/MethodGroupReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/MethodGroupReturnType.cs new file mode 100644 index 000000000..d37a582f3 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/MethodGroupReturnType.cs @@ -0,0 +1,43 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Return type used for MethodGroupResolveResult. + /// + public class MethodGroupReturnType : AbstractReturnType + { + public MethodGroupReturnType() + { + } + + public override IClass GetUnderlyingClass() + { + return null; + } + + public override List GetMethods() + { + return new List(); + } + + public override List GetProperties() + { + return new List(); + } + + public override List GetFields() + { + return new List(); + } + + public override List GetEvents() + { + return new List(); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/NullReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/NullReturnType.cs new file mode 100644 index 000000000..1711a3bda --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/NullReturnType.cs @@ -0,0 +1,38 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// The type of the 'null'/'nothing' literal. + public sealed class NullReturnType : AbstractReturnType + { + private NullReturnType() {} + + public static readonly NullReturnType Instance = new NullReturnType(); + + public override bool Equals(IReturnType o) + { + return o is NullReturnType; + } + + public override int GetHashCode() + { + return 0; + } + + public override bool IsDefaultReturnType { + get { + return false; + } + } + + public override IClass GetUnderlyingClass() { return null; } + public override List GetMethods() { return new List(); } + public override List GetProperties() { return new List(); } + public override List GetFields() { return new List(); } + public override List GetEvents() { return new List(); } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/PointerReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/PointerReturnType.cs new file mode 100644 index 000000000..993acfe10 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/PointerReturnType.cs @@ -0,0 +1,105 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Represents an unmanaged pointer type. + /// + public class PointerReturnType : DecoratingReturnType + { + IReturnType baseType; + + public PointerReturnType(IReturnType baseType) + { + if (baseType == null) + throw new ArgumentNullException("baseType"); + this.baseType = baseType; + } + + public override IReturnType BaseType { + get { return baseType; } + } + + public override T CastToDecoratingReturnType() + { + if (typeof(T) == typeof(PointerReturnType)) { + return (T)(object)this; + } else { + return null; + } + } + + public override IReturnType GetDirectReturnType() + { + IReturnType newBaseType = baseType.GetDirectReturnType(); + if (newBaseType == baseType) + return this; + else + return new PointerReturnType(newBaseType); + } + + public override bool Equals(IReturnType rt) + { + if (rt == null) return false; + PointerReturnType prt = rt.CastToDecoratingReturnType(); + if (prt == null) return false; + return baseType.Equals(prt.baseType); + } + + public override int GetHashCode() + { + unchecked { + return 53 * baseType.GetHashCode(); + } + } + + public override string DotNetName { + get { return baseType.DotNetName + "*"; } + } + + public override string FullyQualifiedName { + get { return baseType.FullyQualifiedName + "*"; } + } + + public override List GetEvents() + { + return new List(); + } + + public override List GetFields() + { + return new List(); + } + + public override List GetMethods() + { + return new List(); + } + + public override List GetProperties() + { + return new List(); + } + + public override IClass GetUnderlyingClass() + { + return null; + } + + public override string Name { + get { return baseType.Name + "*"; } + } + + public override string Namespace { + get { return baseType.Namespace; } + } + + public override int TypeArgumentCount { + get { return 0; } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ProxyReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ProxyReturnType.cs new file mode 100644 index 000000000..6aeca78c7 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ProxyReturnType.cs @@ -0,0 +1,212 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Base class for return types that wrap around other return types. + /// + public abstract class ProxyReturnType : IReturnType + { + public abstract IReturnType BaseType { + get; + } + + public sealed override bool Equals(object obj) + { + return Equals(obj as IReturnType); + } + + public virtual bool Equals(IReturnType other) + { + // this check is necessary because the underlying Equals implementation + // expects to be able to retrieve the base type of "other" - which fails when + // this==other and therefore other.busy. + if (other == this) + return true; + + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.Equals(other) : false; + } + } + + public override int GetHashCode() + { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.GetHashCode() : 0; + } + } + + protected int GetObjectHashCode() + { + return base.GetHashCode(); + } + + // Required to prevent stack overflow on inferrence cycles + [ThreadStatic] static BusyManager _busyManager; + + static BusyManager busyManager { + get { return _busyManager ?? (_busyManager = new BusyManager()); } + } + + public virtual string FullyQualifiedName { + get { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.FullyQualifiedName : "?"; + } + } + } + + public virtual string Name { + get { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.Name : "?"; + } + } + } + + public virtual string Namespace { + get { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.Namespace : "?"; + } + } + } + + public virtual string DotNetName { + get { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.DotNetName : "?"; + } + } + } + + public virtual int TypeArgumentCount { + get { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.TypeArgumentCount : 0; + } + } + } + + public virtual IClass GetUnderlyingClass() + { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.GetUnderlyingClass() : null; + } + } + + public virtual List GetMethods() + { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.GetMethods() : new List(); + } + } + + public virtual List GetProperties() + { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.GetProperties() : new List(); + } + } + + public virtual List GetFields() + { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.GetFields() : new List(); + } + } + + public virtual List GetEvents() + { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.GetEvents() : new List(); + } + } + + public virtual bool IsDefaultReturnType { + get { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.IsDefaultReturnType : false; + } + } + } + + public bool IsDecoratingReturnType() where T : DecoratingReturnType + { + return CastToDecoratingReturnType() != null; + } + + public virtual T CastToDecoratingReturnType() where T : DecoratingReturnType + { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.CastToDecoratingReturnType() : null; + } + } + + + public bool IsArrayReturnType { + get { + return IsDecoratingReturnType(); + } + } + public ArrayReturnType CastToArrayReturnType() + { + return CastToDecoratingReturnType(); + } + + public bool IsGenericReturnType { + get { + return IsDecoratingReturnType(); + } + } + public GenericReturnType CastToGenericReturnType() + { + return CastToDecoratingReturnType(); + } + + public bool IsConstructedReturnType { + get { + return IsDecoratingReturnType(); + } + } + public ConstructedReturnType CastToConstructedReturnType() + { + return CastToDecoratingReturnType(); + } + + public virtual bool? IsReferenceType { + get { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.IsReferenceType : null; + } + } + } + + public virtual IReturnType GetDirectReturnType() + { + IReturnType baseType = BaseType; + using (var l = busyManager.Enter(this)) { + return (l.Success && baseType != null) ? baseType.GetDirectReturnType() : UnknownReturnType.Instance; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ReferenceReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ReferenceReturnType.cs new file mode 100644 index 000000000..208275802 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ReferenceReturnType.cs @@ -0,0 +1,56 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Return type used for "ref" or "out" expressions. + /// + public class ReferenceReturnType : DecoratingReturnType + { + IReturnType baseType; + + public ReferenceReturnType(IReturnType baseType) + { + this.baseType = baseType; + } + + public override IReturnType GetDirectReturnType() + { + if (baseType == null) + return this; + IReturnType newBaseType = baseType.GetDirectReturnType(); + if (newBaseType == baseType) + return this; + else + return new ReferenceReturnType(newBaseType); + } + + public override IReturnType BaseType { + get { return baseType; } + } + + public override bool Equals(IReturnType other) + { + return object.Equals(baseType, other); + } + + public override int GetHashCode() + { + return baseType != null ? baseType.GetHashCode() : 0; + } + + public override T CastToDecoratingReturnType() + { + if (typeof(T) == typeof(ReferenceReturnType)) { + return (T)(object)this; + } else if (baseType != null) { + return baseType.CastToDecoratingReturnType(); + } else { + return null; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SearchClassReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SearchClassReturnType.cs new file mode 100644 index 000000000..860117848 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SearchClassReturnType.cs @@ -0,0 +1,126 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Threading; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// The SearchClassReturnType is used when only a part of the class name is known and the + /// type can only be resolved on demand (the ConvertVisitor uses SearchClassReturnTypes). + /// + public sealed class SearchClassReturnType : ProxyReturnType + { + IClass declaringClass; + IProjectContent pc; + int caretLine; + int caretColumn; + string name; + string shortName; + int typeParameterCount; + bool lookForInnerClassesInDeclaringClass = true; + + public SearchClassReturnType(IProjectContent projectContent, IClass declaringClass, int caretLine, int caretColumn, string name, int typeParameterCount) + { + if (declaringClass == null) + throw new ArgumentNullException("declaringClass"); + this.declaringClass = declaringClass; + this.pc = projectContent; + this.caretLine = caretLine; + this.caretColumn = caretColumn; + this.typeParameterCount = typeParameterCount; + this.name = name; + int pos = name.LastIndexOf('.'); + if (pos < 0) + shortName = name; + else + shortName = name.Substring(pos + 1); + } + + /// + /// Gets/Sets whether to look for inner classes in the declaring class. + /// The default is true. + /// Set this property to false for return types that are used as base type references. + /// + public bool LookForInnerClassesInDeclaringClass { + get { return lookForInnerClassesInDeclaringClass; } + set { + lookForInnerClassesInDeclaringClass = value; + ClearCachedBaseType(); + } + } + + volatile IReturnType cachedBaseType; + + //int isSearching; // 0=false, 1=true + + // Required to prevent stack overflow on inferrence cycles + // replaces old 'isSearching' flag which wasn't thread-safe + [ThreadStatic] static BusyManager _busyManager; + + static BusyManager busyManager { + get { return _busyManager ?? (_busyManager = new BusyManager()); } + } + + void ClearCachedBaseType() + { + cachedBaseType = null; + } + + public override IReturnType BaseType { + get { + IReturnType type = cachedBaseType; + if (type != null) + return type; + using (var l = busyManager.Enter(this)) { + // abort if called recursively on the same thread + if (!l.Success) + return null; + SearchTypeRequest request = new SearchTypeRequest(name, typeParameterCount, declaringClass, caretLine, caretColumn); + if (!lookForInnerClassesInDeclaringClass) { + // skip looking for inner classes by adjusting the CurrentType for the lookup + request.CurrentType = declaringClass.DeclaringType; + } + type = pc.SearchType(request).Result; + cachedBaseType = type; + if (type != null) + DomCache.RegisterForClear(ClearCachedBaseType); + return type; + } + } + } + + public override string FullyQualifiedName { + get { + string tmp = base.FullyQualifiedName; + if (tmp == "?") { + return name; + } + return tmp; + } + } + + public override string Name { + get { + return shortName; + } + } + + public override string DotNetName { + get { + string tmp = base.DotNetName; + if (tmp == "?") { + return name; + } + return tmp; + } + } + + public override string ToString() + { + return String.Format("[SearchClassReturnType: {0}]", name); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SystemTypes.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SystemTypes.cs new file mode 100644 index 000000000..5d6d63908 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SystemTypes.cs @@ -0,0 +1,135 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class SystemTypes + { + public readonly IReturnType Void; + public readonly IReturnType Object; + public readonly IReturnType Delegate; + public readonly IReturnType MulticastDelegate; + public readonly IReturnType ValueType; + public readonly IReturnType Enum; + + public readonly IReturnType Boolean; + public readonly IReturnType Int32; + public readonly IReturnType String; + + public readonly IReturnType Array; + public readonly IReturnType Attribute; + public readonly IReturnType Type; + + public readonly IReturnType Exception; + public readonly IReturnType AsyncCallback; + public readonly IReturnType IAsyncResult; + public readonly IReturnType IDisposable; + + IProjectContent pc; + + public SystemTypes(IProjectContent pc) + { + this.pc = pc; + Void = new VoidReturnType(pc); + Object = CreateFromName("System.Object"); + Delegate = CreateFromName("System.Delegate"); + MulticastDelegate = CreateFromName("System.MulticastDelegate"); + ValueType = CreateFromName("System.ValueType"); + Enum = CreateFromName("System.Enum"); + + Boolean = CreateFromName("System.Boolean"); + Int32 = CreateFromName("System.Int32"); + String = CreateFromName("System.String"); + + Array = CreateFromName("System.Array"); + Attribute = CreateFromName("System.Attribute"); + Type = CreateFromName("System.Type"); + + Exception = CreateFromName("System.Exception"); + AsyncCallback = CreateFromName("System.AsyncCallback"); + IAsyncResult = CreateFromName("System.IAsyncResult"); + IDisposable = CreateFromName("System.IDisposable"); + } + + IReturnType CreateFromName(string name) + { + IClass c = pc.GetClass(name, 0); + if (c != null) { + return c.DefaultReturnType; + } else { + LoggingService.Warn("SystemTypes.CreateFromName could not find " + name); + return Void; + } + } + + /// + /// Creates the return type for a primitive system type. + /// + public IReturnType CreatePrimitive(Type type) + { + if (type.HasElementType || type.ContainsGenericParameters) { + throw new ArgumentException("Only primitive types are supported."); + } + return CreateFromName(type.FullName); + } + } + + public sealed class VoidClass : DefaultClass + { + internal static readonly string VoidName = typeof(void).FullName; + + public VoidClass(IProjectContent pc) + : base(new DefaultCompilationUnit(pc), VoidName) + { + this.ClassType = ClassType.Struct; + this.Modifiers = ModifierEnum.Public | ModifierEnum.Sealed; + Freeze(); + } + + protected override IReturnType CreateDefaultReturnType() + { + return ProjectContent.SystemTypes.Void; + } + } + + public sealed class VoidReturnType : AbstractReturnType + { + IProjectContent pc; + + public VoidReturnType(IProjectContent pc) + { + if (pc == null) + throw new ArgumentNullException("pc"); + this.pc = pc; + FullyQualifiedName = VoidClass.VoidName; + } + + public override IClass GetUnderlyingClass() + { + return pc.GetClass("System.Void", 0, LanguageProperties.CSharp, GetClassOptions.LookInReferences); + } + + public override List GetMethods() + { + return new List(); + } + + public override List GetProperties() + { + return new List(); + } + + public override List GetFields() + { + return new List(); + } + + public override List GetEvents() + { + return new List(); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/UnknownReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/UnknownReturnType.cs new file mode 100644 index 000000000..5d06f332b --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/UnknownReturnType.cs @@ -0,0 +1,18 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + sealed class UnknownReturnType : ProxyReturnType + { + public static readonly UnknownReturnType Instance = new UnknownReturnType(); + + public override IReturnType BaseType { + get { + return null; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ClassType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ClassType.cs new file mode 100644 index 000000000..f179a1703 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ClassType.cs @@ -0,0 +1,14 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +namespace ICSharpCode.SharpDevelop.Dom +{ + public enum ClassType { + Class = ICSharpCode.NRefactory.Ast.ClassType.Class, + Enum = ICSharpCode.NRefactory.Ast.ClassType.Enum, + Interface = ICSharpCode.NRefactory.Ast.ClassType.Interface, + Struct = ICSharpCode.NRefactory.Ast.ClassType.Struct, + Delegate = 0x5, + Module = ICSharpCode.NRefactory.Ast.ClassType.Module + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ExplicitInterfaceImplementation.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ExplicitInterfaceImplementation.cs new file mode 100644 index 000000000..dc563c1c0 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ExplicitInterfaceImplementation.cs @@ -0,0 +1,51 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public sealed class ExplicitInterfaceImplementation : Immutable, IEquatable + { + readonly IReturnType interfaceReference; + readonly string memberName; + + public ExplicitInterfaceImplementation(IReturnType interfaceReference, string memberName) + { + this.interfaceReference = interfaceReference; + this.memberName = memberName; + } + + public IReturnType InterfaceReference { + get { return interfaceReference; } + } + + public string MemberName { + get { return memberName; } + } + + public ExplicitInterfaceImplementation Clone() + { + return this; // object is immutable, no Clone() required + } + + public override int GetHashCode() + { + return interfaceReference.GetHashCode() ^ memberName.GetHashCode(); + } + + public override bool Equals(object obj) + { + return Equals(obj as ExplicitInterfaceImplementation); + } + + public bool Equals(ExplicitInterfaceImplementation other) + { + if (other == null) + return false; + else + return this.interfaceReference == other.interfaceReference && this.memberName == other.memberName; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IAttribute.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IAttribute.cs new file mode 100644 index 000000000..969101c10 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IAttribute.cs @@ -0,0 +1,55 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface IAttribute : IFreezable + { + /// + /// Gets the compilation unit in which this attribute is defined. + /// + ICompilationUnit CompilationUnit { + get; + } + + /// + /// Gets the code region of this attribute. + /// + DomRegion Region { + get; + } + + AttributeTarget AttributeTarget { + get; + } + + IReturnType AttributeType { + get; + } + + IList PositionalArguments { + get; + } + + IDictionary NamedArguments { + get; + } + } + + public enum AttributeTarget + { + None, + Assembly, + Field, + Event, + Method, + Module, + Param, + Property, + Return, + Type + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IClass.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IClass.cs new file mode 100644 index 000000000..3bee9bd95 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IClass.cs @@ -0,0 +1,150 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface IClass : IEntity + { + /// + /// Region of the whole class including the body. + /// + DomRegion Region { + get; + } + + /// + /// The default return type to use for this class. + /// This property is mutable even when the IClass is frozen, see + /// documentation for . + /// This property is thread-safe. + /// + IReturnType DefaultReturnType { get; } + + ClassType ClassType { + get; + } + + /// + /// Gets the using scope of contains this class. + /// + IUsingScope UsingScope { + get; + } + + IList BaseTypes { + get; + } + + IList InnerClasses { + get; + } + + IList Fields { + get; + } + + IList Properties { + get; + } + + IList Methods { + get; + } + + IList Events { + get; + } + + IEnumerable AllMembers { + get; + } + + IList TypeParameters { + get; + } + + /// + /// Returns the list of all classes that this class inherits from (directly or indirectly). + /// If this property is used on part of a partial class, it will also return classes inherited in other parts. + /// + IEnumerable ClassInheritanceTree { + get; + } + + IEnumerable ClassInheritanceTreeClassesOnly { + get; + } + + IClass BaseClass { + get; + } + + IReturnType BaseType { + get; + } + + /// + /// If this is a partial class, gets the compound class containing information from all parts. + /// If this is not a partial class, a reference to this class is returned. + /// + IClass GetCompoundClass(); + + IClass GetInnermostClass(int caretLine, int caretColumn); + + List GetAccessibleTypes(IClass callingClass); + + /// + /// Searches the member with the specified name. Returns the first member/overload found. + /// + IMember SearchMember(string memberName, LanguageProperties language); + + /// Return true if the specified class is a base class of this class; otherwise return false. + /// Returns false when possibleBaseClass is null. + bool IsTypeInInheritanceTree(IClass possibleBaseClass); + + bool HasPublicOrInternalStaticMembers { + get; + } + bool HasExtensionMethods { + get; + } + + bool IsPartial { + get; + } + + /// + /// Gets/sets if this class has an associated compound class. + /// This property is mutable even if the IClass instance is frozen. + /// This property is thread-safe. + /// + /// This property may only be set by the IProjectContent implementation to which this class is added. + /// + /// Rational: some languages support partial classes where only one of the parts needs + /// the "partial" modifier. If the part without the modifier is added to the project + /// content first, it is added without compound class and may get a DefaultReturnType that refers + /// to itself. + /// However, when the other part with the modifier is added, a compound class is created, and the + /// DefaultReturnType of this class must change even though it is frozen. + /// + bool HasCompoundClass { + get; + set; + } + + /// + /// Gets whether a default constructor should be added to this class if it is required. + /// Such automatic default constructors will not appear in IClass.Methods, but will be present + /// in IClass.DefaultReturnType.GetMethods(). + /// + /// This way of creating the default constructor is necessary because + /// we cannot create it directly in the IClass - we need to consider partial classes. + bool AddDefaultConstructorIfRequired { + get; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ICompilationUnit.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ICompilationUnit.cs new file mode 100644 index 000000000..5de723a31 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ICompilationUnit.cs @@ -0,0 +1,74 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface ICompilationUnit : IFreezable + { + string FileName { + get; + set; + } + + bool ErrorsDuringCompile { + get; + set; + } + + object Tag { + get; + set; + } + + IProjectContent ProjectContent { + get; + } + + /// + /// Gets the language this compilation unit was written in. + /// + LanguageProperties Language { + get; + } + + /// + /// Gets the main using scope of the compilation unit. + /// That scope usually represents the root namespace. + /// + IUsingScope UsingScope { + get; + } + + IList Attributes { + get; + } + + IList Classes { + get; + } + + IList MiscComments { + get; + } + + IList DokuComments { + get; + } + + IList TagComments { + get; + } + + IList FoldingRegions { + get; + } + + /// + /// Returns the innermost class in which the carret currently is, returns null + /// if the carret is outside any class boundaries. + /// + IClass GetInnermostClass(int caretLine, int caretColumn); + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ICompletionEntry.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ICompletionEntry.cs new file mode 100644 index 000000000..287ef1f36 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ICompletionEntry.cs @@ -0,0 +1,18 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Element of namespace or Dom completion list. + /// + public interface ICompletionEntry + { + string Name { + get; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IDomProgressMonitor.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IDomProgressMonitor.cs new file mode 100644 index 000000000..8170a08bf --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IDomProgressMonitor.cs @@ -0,0 +1,23 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// This is a basic interface to a "progress bar" type of + /// control. + /// + public interface IDomProgressMonitor + { + /// + /// Gets/sets if the task current shows a modal dialog. Set this property to true to make progress + /// dialogs windows temporarily invisible while your modal dialog is showing. + /// + bool ShowingDialog { + get; + set; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IEntity.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IEntity.cs new file mode 100644 index 000000000..5fdb21f49 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IEntity.cs @@ -0,0 +1,171 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface IEntity : ICompletionEntry, IFreezable, IComparable + { + string FullyQualifiedName { + get; + } + + string Namespace { + get; + } + + /// + /// The fully qualified name in the internal .NET notation (with `1 for generic types) + /// + string DotNetName { + get; + } + + DomRegion BodyRegion { + get; + } + + /// + /// Gets the declaring type. + /// For members, this is the type that contains the member. + /// For classes, this is the outer class (for nested classes), or null if there this + /// is a top-level class. + /// + IClass DeclaringType { + get; + } + + ModifierEnum Modifiers { + get; + } + + IList Attributes { + get; + } + + string Documentation { + get; + } + + /// + /// Returns true if this entity has the 'abstract' modifier set. + /// (Returns false for interface members). + /// + bool IsAbstract { + get; + } + + bool IsSealed { + get; + } + + /// + /// Gets whether this entity is static. + /// Returns true if either the 'static' or the 'const' modifier is set. + /// + bool IsStatic { + get; + } + + /// + /// Gets whether this entity is a constant (C#-like const). + /// + bool IsConst { + 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 already overriding a method. + /// + bool IsVirtual { + get; + } + + bool IsPublic { + get; + } + + bool IsProtected { + get; + } + + bool IsPrivate { + get; + } + + bool IsInternal { + get; + } + + bool IsReadonly { + get; + } + + [Obsolete("This property does not do what one would expect - it merely checks if protected+internal are set, it is not the equivalent of AssemblyAndFamily in Reflection!")] + bool IsProtectedAndInternal { + get; + } + + [Obsolete("This property does not do what one would expect - it merely checks if one of protected+internal is set, it is not the equivalent of AssemblyOrFamily in Reflection!")] + bool IsProtectedOrInternal { + get; + } + + bool IsOverride { + get; + } + /// + /// Gets if the member can be overridden. Returns true when the member is "virtual" or "override" but not "sealed". + /// + bool IsOverridable { + get; + } + + bool IsNew { + get; + } + bool IsSynthetic { + get; + } + + /// + /// Gets the compilation unit that contains this entity. + /// + ICompilationUnit CompilationUnit { + get; + } + + /// + /// The project content in which this entity is defined. + /// + IProjectContent ProjectContent { + get; + } + + /// + /// This property can be used to attach any user-defined data to this class/method. + /// This property is mutable, it can be changed when the class/method is frozen. + /// + object UserData { + get; + set; + } + + EntityType EntityType { + get; + } + + bool IsAccessible(IClass callingClass, bool isAccessThoughReferenceOfCurrentClass); + } + + public enum EntityType { + Class, + Field, + Property, + Method, + Event + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IEvent.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IEvent.cs new file mode 100644 index 000000000..ef440b7cb --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IEvent.cs @@ -0,0 +1,22 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface IEvent : IMember + { + IMethod AddMethod { + get; + } + + IMethod RemoveMethod { + get; + } + + IMethod RaiseMethod { + get; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IField.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IField.cs new file mode 100644 index 000000000..cc6fc5287 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IField.cs @@ -0,0 +1,16 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface IField : IMember + { + /// Gets if this field is a local variable that has been converted into a field. + bool IsLocalVariable { get; } + + /// Gets if this field is a parameter that has been converted into a field. + bool IsParameter { get; } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IFreezable.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IFreezable.cs new file mode 100644 index 000000000..1beed3dc9 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IFreezable.cs @@ -0,0 +1,103 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace ICSharpCode.SharpDevelop.Dom +{ + 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(); + } + + /// + /// Base class for immutable objects. Provides implementation for IFreezable that reports the + /// object as always-frozen. + /// + public abstract class Immutable : IFreezable + { + bool IFreezable.IsFrozen { + get { return true; } + } + + void IFreezable.Freeze() + { + } + } + + 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() + { + } + + protected void CheckBeforeMutation() + { + if (isFrozen) + throw new InvalidOperationException("Cannot mutate frozen " + GetType().Name); + } + + protected static IList FreezeList(IList list) where T : IFreezable + { + if (list == null || list.Count == 0) + return EmptyList.Instance; + list = new ReadOnlyCollection(list.ToArray()); + foreach (T item in list) { + item.Freeze(); + } + return list; + } + + protected static IList FreezeList(IList list) + { + if (list == null || list.Count == 0) + return EmptyList.Instance; + else + return new ReadOnlyCollection(list.ToArray()); + } + + protected static IList FreezeList(IList list) + { + if (list == null || list.Count == 0) + return EmptyList.Instance; + else + return new ReadOnlyCollection(list.ToArray()); + } + } + + static class EmptyList + { + public static readonly ReadOnlyCollection Instance = new ReadOnlyCollection(new T[0]); + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IMember.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IMember.cs new file mode 100644 index 000000000..837c9c688 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IMember.cs @@ -0,0 +1,54 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface IMember : IEntity, ICloneable + { + /// + /// Declaration region of the member (without body!) + /// + DomRegion Region { + get; + } + + /// + /// Gets/Sets the declaring type reference (declaring type incl. type arguments). + /// Never returns null. + /// If the property is set to null (e.g. when this is not a specialized member), + /// it should return the default type reference to the . + /// + IReturnType DeclaringTypeReference { + get; + set; + } + + /// + /// Gets the generic member this member is based on. + /// Returns null if this is not a specialized member. + /// Specialized members are the result of overload resolution with type substitution. + /// + IMember GenericMember { + get; + } + + /// + /// Creates a copy of this member with its GenericMember property set to this member. + /// Use this method to create copies of a member that should be regarded as the "same member" + /// for refactoring purposes. + /// + IMember CreateSpecializedMember(); + + IReturnType ReturnType { + get; + set; + } + + IList InterfaceImplementations { + get; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IMethod.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IMethod.cs new file mode 100644 index 000000000..d5eabb05c --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IMethod.cs @@ -0,0 +1,38 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface IMethodOrProperty : IMember + { + IList Parameters { + get; + } + + bool IsExtensionMethod { + get; + } + } + + public interface IMethod : IMethodOrProperty + { + IList TypeParameters { + get; + } + + bool IsConstructor { + get; + } + + IList HandlesClauses { + get; + } + + bool IsOperator { + get; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IParameter.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IParameter.cs new file mode 100644 index 000000000..d1882879e --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IParameter.cs @@ -0,0 +1,53 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + + public interface IParameter : IFreezable, IComparable + { + string Name { + get; + } + + IReturnType ReturnType { + get; + set; + } + + IList Attributes { + get; + } + + ParameterModifiers Modifiers { + get; + } + + DomRegion Region { + get; + } + + string Documentation { + get; + } + + bool IsOut { + get; + } + + bool IsRef { + get; + } + + bool IsParams { + get; + } + + bool IsOptional { + get; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IProperty.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IProperty.cs new file mode 100644 index 000000000..ce70c2850 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IProperty.cs @@ -0,0 +1,38 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface IProperty : IMethodOrProperty + { + DomRegion GetterRegion { + get; + } + + DomRegion SetterRegion { + get; + } + + bool CanGet { + get; + } + + bool CanSet { + get; + } + + bool IsIndexer { + get; + } + + ModifierEnum GetterModifiers { + get; + } + + ModifierEnum SetterModifiers { + get; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IReturnType.cs new file mode 100644 index 000000000..ce4624042 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IReturnType.cs @@ -0,0 +1,145 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Interface for reference to types (classes). + /// Such a reference can be direct (DefaultReturnType), lazy (SearchClassReturnType) or + /// returns types that stand for special references (e.g. ArrayReturnType) + /// + public interface IReturnType : IEquatable + { + /// + /// 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> + ///
+ string FullyQualifiedName { + get; + } + + /// + /// Gets the short name of the class the return type is pointing to. + /// + /// + /// "Int32" or "int" (depending how the return type was created) for int[]
+ /// "List" for List<string> + ///
+ string Name { + get; + } + + /// + /// Gets the namespace of the class the return type is pointing to. + /// + /// + /// "System" for int[]
+ /// "System.Collections.Generic" for List<string> + ///
+ string Namespace { + get; + } + + /// + /// Gets the full dotnet name of the return type. The DotnetName is used for the + /// documentation tags. + /// + /// + /// "System.Int[]" for int[]
+ /// "System.Collections.Generic.List{System.String}" for List<string> + ///
+ string DotNetName { + get; + } + + /// + /// Gets the number of type parameters the target class should have + /// / the number of type arguments specified by this type reference. + /// + int TypeArgumentCount { + get; + } + + /// + /// Gets the underlying class of this return type. This method will return null for + /// generic return types and types that cannot be resolved. + /// + IClass GetUnderlyingClass(); + + /// + /// Gets all methods that can be called on this return type. + /// + List GetMethods(); + + /// + /// Gets all properties that can be called on this return type. + /// + List GetProperties(); + + /// + /// Gets all fields that can be called on this return type. + /// + List GetFields(); + + /// + /// Gets all events that can be called on this return type. + /// + List GetEvents(); + + + /// + /// Gets if the return type is a default type, i.e. no array, generic etc. + /// + /// + /// True for SearchClassReturnType, GetClassReturnType and DefaultReturnType.
+ /// False for ArrayReturnType, SpecificReturnType etc. + ///
+ bool IsDefaultReturnType { get; } + + /// + /// Gets if the cast to the specified decorating return type would be valid. + /// + bool IsDecoratingReturnType() where T : DecoratingReturnType; + + /// + /// Casts this return type to the decorating return type specified as type parameter. + /// This methods casts correctly even when the return type is wrapped by a ProxyReturnType. + /// When the cast is invalid, null is returned. + /// + T CastToDecoratingReturnType() where T : DecoratingReturnType; + + bool IsArrayReturnType { get; } + ArrayReturnType CastToArrayReturnType(); + + bool IsGenericReturnType { get; } + GenericReturnType CastToGenericReturnType(); + + bool IsConstructedReturnType { get; } + ConstructedReturnType CastToConstructedReturnType(); + + /// + /// 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. generic type argument or type not found) + /// + bool? IsReferenceType { get; } + + /// + /// Gets an identical return type that binds directly to the underlying class, so + /// that repeatedly calling methods does not cause repeated class lookups. + /// The direct return type will always point to the old version of the class, so don't + /// store direct return types! + /// + /// This method never returns null. + IReturnType GetDirectReturnType(); + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ITypeParameter.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ITypeParameter.cs new file mode 100644 index 000000000..48f9f18f8 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ITypeParameter.cs @@ -0,0 +1,71 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Type parameter of a generic class/method. + /// + public interface ITypeParameter : IFreezable + { + /// + /// The name of the type parameter (for example "T") + /// + string Name { get; } + + /// + /// Gets the index of the type parameter in the type parameter list of the owning method/class. + /// + int Index { get; } + + IList Attributes { get; } + + /// + /// The method this type parameter is defined for. + /// This property is null when the type parameter is for a class. + /// + IMethod Method { get; } + + /// + /// The class this type parameter is defined for. + /// When the type parameter is defined for a method, this is the class containing + /// that method. + /// + IClass Class { get; } + + /// + /// Gets the contraints of this type parameter. + /// + IList Constraints { get; } + + /// + /// Gets if the type parameter has the 'new()' constraint. + /// + bool HasConstructableConstraint { get; } + + /// + /// Gets if the type parameter has the 'class' constraint. + /// + bool HasReferenceTypeConstraint { get; } + + /// + /// Gets if the type parameter has the 'struct' constraint. + /// + bool HasValueTypeConstraint { get; } + + /// + /// Gets the type that was used to bind this type parameter. + /// This property returns null for generic methods/classes, it + /// is non-null only for constructed versions of generic methods. + /// + IReturnType BoundTo { get; } + + /// + /// If this type parameter was bound, returns the unbound version of it. + /// + ITypeParameter UnboundTypeParameter { get; } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IUsing.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IUsing.cs new file mode 100644 index 000000000..e38a18c57 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IUsing.cs @@ -0,0 +1,41 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface IUsing : IFreezable + { + DomRegion Region { + get; + } + + IList Usings { + get; + } + + bool HasAliases { get; } + + void AddAlias(string alias, IReturnType type); + + /// + /// Gets the list of aliases. Can be null when there are no aliases! + /// + IDictionary Aliases { + get; + } + + /// + /// Returns a collection of possible types that could be meant when using this Import + /// to search the type. + /// Types with the incorrect type parameter count might be returned, but for each + /// same using entry or alias entry at most one (the best matching) type should be returned. + /// + /// An IEnumerable with zero or more non-null return types. + IEnumerable SearchType(string partialTypeName, int typeParameterCount); + + string SearchNamespace(string partialNamespaceName); + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IUsingScope.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IUsingScope.cs new file mode 100644 index 000000000..64bec2d06 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IUsingScope.cs @@ -0,0 +1,42 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// A scope that can contain using declarations. + /// In C#, every file is a using scope, and every "namespace" declaration inside + /// the file is a nested using scope. + /// + public interface IUsingScope : IFreezable + { + /// + /// Gets the region of the using scope. + /// + DomRegion Region { get; } + + /// + /// Gets the parent scope. + /// Returns null if this is a root scope. + /// + IUsingScope Parent { get; } + + /// + /// Gets the usings in this using scope. + /// + IList Usings { get; } + + /// + /// Gets the list of child scopes. Child scopes usually represent "namespace" declarations. + /// + IList ChildScopes { get; } + + /// + /// Gets the name of the namespace represented by the using scope. + /// + string NamespaceName { get; } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ModifierEnum.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ModifierEnum.cs new file mode 100644 index 000000000..c1eef4253 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ModifierEnum.cs @@ -0,0 +1,46 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using M = ICSharpCode.NRefactory.Ast.Modifiers; + +namespace ICSharpCode.SharpDevelop.Dom +{ + [Flags] + public enum ModifierEnum // must be the same values as NRefactories' ModifierEnum + { + None = 0, + + // Access + Private = M.Private, + Internal = M.Internal, // == Friend + Protected = M.Protected, + Public = M.Public, + Dim = M.Dim, // VB.NET SPECIFIC + + // Scope + Abstract = M.Abstract, // == MustOverride/MustInherit + Virtual = M.Virtual, + Sealed = M.Sealed, + Static = M.Static, + Override = M.Override, + Readonly = M.ReadOnly, + Const = M.Const, + New = M.New, // == Shadows + Partial = M.Partial, + + // Special + Extern = M.Extern, + Volatile = M.Volatile, + Unsafe = M.Unsafe, + Overloads = M.Overloads, // VB specific + WithEvents = M.WithEvents, // VB specific + Default = M.Default, // VB specific + Fixed = M.Fixed, + + Synthetic = M.Synthetic, + + ProtectedAndInternal = Internal | Protected, + VisibilityMask = Private | Internal | Protected | Public, + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ParameterModifiers.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ParameterModifiers.cs new file mode 100644 index 000000000..a28bf5a40 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ParameterModifiers.cs @@ -0,0 +1,20 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + [Serializable] + [Flags] + public enum ParameterModifiers : byte + { + // Values must be the same as in NRefactory's ParamModifiers + None = 0, + In = 1, + Out = 2, + Ref = 4, + Params = 8, + Optional = 16 + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/Region.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/Region.cs new file mode 100644 index 000000000..f1f6ba1a9 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/Region.cs @@ -0,0 +1,129 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using Location = ICSharpCode.NRefactory.Location; + +namespace ICSharpCode.SharpDevelop.Dom +{ + [Serializable] + public struct DomRegion : IEquatable + { + readonly int beginLine; + readonly int endLine; + readonly int beginColumn; + readonly int endColumn; + + public readonly static DomRegion Empty = new DomRegion(-1, -1); + + public bool IsEmpty { + get { + return BeginLine <= 0; + } + } + + public int BeginLine { + get { + return beginLine; + } + } + + /// + /// if the end line is == -1 the end column is -1 too + /// this stands for an unknwon end + /// + public int EndLine { + get { + return endLine; + } + } + + public int BeginColumn { + get { + return beginColumn; + } + } + + /// + /// if the end column is == -1 the end line is -1 too + /// this stands for an unknown end + /// + public int EndColumn { + get { + return endColumn; + } + } + + public static DomRegion FromLocation(Location start, Location end) + { + return new DomRegion(start.Y, start.X, end.Y, end.X); + } + + public DomRegion(int beginLine, int beginColumn, int endLine, int endColumn) + { + this.beginLine = beginLine; + this.beginColumn = beginColumn; + this.endLine = endLine; + this.endColumn = endColumn; + } + + public DomRegion(int beginLine, int beginColumn) + { + this.beginLine = beginLine; + this.beginColumn = beginColumn; + this.endLine = -1; + this.endColumn = -1; + } + + /// + /// Returns true, if the given coordinates (row, column) are in the region. + /// This method assumes that for an unknown end the end line is == -1 + /// + public bool IsInside(int row, int column) + { + if (IsEmpty) + return false; + return row >= BeginLine && + (row <= EndLine || EndLine == -1) && + (row != BeginLine || column >= BeginColumn) && + (row != EndLine || column <= EndColumn); + } + + public override string ToString() + { + return String.Format("[Region: BeginLine = {0}, EndLine = {1}, BeginColumn = {2}, EndColumn = {3}]", + beginLine, + endLine, + beginColumn, + endColumn); + } + + public override bool Equals(object obj) + { + return obj is DomRegion && Equals((DomRegion)obj); + } + + public override int GetHashCode() + { + unchecked { + return BeginColumn + 1100009 * BeginLine + 1200007 * BeginColumn + 1300021 * EndColumn; + } + } + + public bool Equals(DomRegion other) + { + return BeginLine == other.BeginLine && BeginColumn == other.BeginColumn + && EndLine == other.EndLine && EndColumn == other.EndColumn; + } + + public static bool operator ==(DomRegion lhs, DomRegion rhs) + { + return lhs.Equals(rhs); + } + + public static bool operator !=(DomRegion lhs, DomRegion rhs) + { + return !lhs.Equals(rhs); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs new file mode 100644 index 000000000..a2905afd8 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs @@ -0,0 +1,556 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using ICSharpCode.SharpDevelop.Dom.Refactoring; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class LanguageProperties + { + /// + /// A case-sensitive dummy language that returns false for all Supports.. properties, + /// uses a dummy code generator and refactoring provider and returns null for CodeDomProvider. + /// + public readonly static LanguageProperties None = new LanguageProperties(StringComparer.Ordinal); + + /// + /// C# 3.0 language properties. + /// + public readonly static LanguageProperties CSharp = new CSharpProperties(); + + /// + /// VB.Net 8 language properties. + /// + public readonly static LanguageProperties VBNet = new VBNetProperties(); + + public LanguageProperties(StringComparer nameComparer) + { + this.nameComparer = nameComparer; + } + + #region Language-specific service providers + readonly StringComparer nameComparer; + + public StringComparer NameComparer { + get { + return nameComparer; + } + } + + public virtual CodeGenerator CodeGenerator { + get { + return CodeGenerator.DummyCodeGenerator; + } + } + + public virtual RefactoringProvider RefactoringProvider { + get { + return RefactoringProvider.DummyProvider; + } + } + + /// + /// Gets the ambience for this language. Because the IAmbience interface is not thread-safe, every call + /// creates a new instance. + /// + public virtual IAmbience GetAmbience() + { + return new CSharp.CSharpAmbience(); + } + + /// + /// Gets the CodeDomProvider for this language. Can return null! + /// + public virtual System.CodeDom.Compiler.CodeDomProvider CodeDomProvider { + get { + return null; + } + } + + sealed class DummyCodeDomProvider : System.CodeDom.Compiler.CodeDomProvider + { + public static readonly DummyCodeDomProvider Instance = new DummyCodeDomProvider(); + + [Obsolete("Callers should not use the ICodeGenerator interface and should instead use the methods directly on the CodeDomProvider class.")] + public override System.CodeDom.Compiler.ICodeGenerator CreateGenerator() + { + return null; + } + + [Obsolete("Callers should not use the ICodeCompiler interface and should instead use the methods directly on the CodeDomProvider class.")] + public override System.CodeDom.Compiler.ICodeCompiler CreateCompiler() + { + return null; + } + } + #endregion + + #region Supports... + /// + /// Gets if the language supports calling C# 3-style extension methods + /// (first parameter = instance parameter) + /// + public virtual bool SupportsExtensionMethods { + get { return false; } + } + + /// + /// Gets if the language supports calling extension properties + /// (first parameter = instance parameter) + /// + public virtual bool SupportsExtensionProperties { + get { return false; } + } + + /// + /// Gets if extension methods/properties are searched in imported classes (returns true) or if + /// only the extensions from the current class, imported classes and imported modules are used + /// (returns false). This property has no effect if the language doesn't support extension methods or properties. + /// + public virtual bool SearchExtensionsInClasses { + get { return false; } + } + + /// + /// Gets if namespaces are imported (i.e. Imports System, Dim a As Collections.ArrayList) + /// + public virtual bool ImportNamespaces { + get { return false; } + } + + /// + /// Gets if modules are imported with their namespace (i.e. Microsoft.VisualBasic.Randomize()). + /// + public virtual bool ImportModules { + get { return false; } + } + + /// + /// Gets if classes can be imported (i.e. Imports System.Math) + /// + public virtual bool CanImportClasses { + get { return false; } + } + + /// + /// Gets if the language allows partial classes where the partial modifier is not + /// used on any part. + /// + public virtual bool ImplicitPartialClasses { + get { return false; } + } + + /// + /// Allow invoking an object constructor outside of ExpressionContext.ObjectCreation. + /// Used for Boo, which creates instances like this: 'self.Size = Size(10, 20)' + /// + public virtual bool AllowObjectConstructionOutsideContext { + get { return false; } + } + + /// + /// Gets if the language supports implicit interface implementations. + /// + public virtual bool SupportsImplicitInterfaceImplementation { + get { return false; } + } + + /// + /// Gets if the language enforces that explicit interface implementations are uncallable except through + /// the interface itself. + /// If this property is false, code generators may assume that multiple explicit interface implementations + /// with conflicting return types are invalid unless they are renamed. + /// + public virtual bool ExplicitInterfaceImplementationIsPrivateScope { + get { return false; } + } + + /// + /// Gets if events explicitly implementing an interface require add {} remove {} regions. + /// + public virtual bool RequiresAddRemoveRegionInExplicitInterfaceImplementation { + get { return false; } + } + + /// + /// Gets the start token of an indexer expression in the language. Usually '[' or '('. + /// + public virtual string IndexerExpressionStartToken { + get { return "["; } + } + + public virtual TextFinder GetFindClassReferencesTextFinder(IClass c) + { + // when finding attribute references, also look for the short form of the name + if (c.Name.Length > 9 && nameComparer.Equals(c.Name.Substring(c.Name.Length - 9), "Attribute")) { + return new CombinedTextFinder( + new WholeWordTextFinder(c.Name.Substring(0, c.Name.Length - 9), nameComparer), + new WholeWordTextFinder(c.Name, nameComparer) + ); + } + return new WholeWordTextFinder(c.Name, nameComparer); + } + + public virtual TextFinder GetFindMemberReferencesTextFinder(IMember member) + { + IProperty property = member as IProperty; + if (property != null && property.IsIndexer) { + return new IndexBeforeTextFinder(IndexerExpressionStartToken); + } else { + return new WholeWordTextFinder(member.Name, nameComparer); + } + } + + public virtual bool IsClassWithImplicitlyStaticMembers(IClass c) + { + return false; + } + #endregion + + #region Code-completion filters + public virtual bool ShowInNamespaceCompletion(IClass c) + { + return true; + } + + public virtual bool ShowMember(IMember member, bool showStatic) + { + IProperty property = member as IProperty; + if (property != null && property.IsIndexer) { + return false; + } + IMethod method = member as IMethod; + if (method != null && (method.IsConstructor || method.IsOperator)) { + return false; + } + return member.IsStatic == showStatic; + } + + public virtual bool ShowMemberInOverrideCompletion(IMember member) + { + return true; + } + #endregion + + /// + /// Generates the default imports statements a new application for this language should use. + /// + public virtual IUsing CreateDefaultImports(IProjectContent pc) + { + return null; + } + + public override string ToString() + { + return "[" + base.ToString() + "]"; + } + + public static LanguageProperties GetLanguage(string language) + { + switch(language) + { + case "VBNet": + case "VB": + return LanguageProperties.VBNet; + default: + return LanguageProperties.CSharp; + } + } + + #region CSharpProperties + internal sealed class CSharpProperties : LanguageProperties + { + public CSharpProperties() : base(StringComparer.Ordinal) {} + + public override RefactoringProvider RefactoringProvider { + get { + return NRefactoryRefactoringProvider.NRefactoryCSharpProviderInstance; + } + } + + public override CodeGenerator CodeGenerator { + get { + return CSharpCodeGenerator.Instance; + } + } + + public override System.CodeDom.Compiler.CodeDomProvider CodeDomProvider { + get { + return new Microsoft.CSharp.CSharpCodeProvider(); + } + } + + public override bool SupportsImplicitInterfaceImplementation { + get { return true; } + } + + public override bool ExplicitInterfaceImplementationIsPrivateScope { + get { return true; } + } + + /// + /// Gets if events explicitly implementing an interface require add {} remove {} regions. + /// + public override bool RequiresAddRemoveRegionInExplicitInterfaceImplementation { + get { return true; } + } + + public override bool SupportsExtensionMethods { + get { return true; } + } + + public override bool SearchExtensionsInClasses { + get { return true; } + } + + public override string ToString() + { + return "[LanguageProperties: C#]"; + } + + public override TextFinder GetFindMemberReferencesTextFinder(IMember member) + { + IMethod method = member as IMethod; + if (method != null && method.IsConstructor) { + return new CombinedTextFinder( + new WholeWordTextFinder(member.DeclaringType.Name, this.NameComparer), + new WholeWordTextFinder("this", this.NameComparer), + new WholeWordTextFinder("base", this.NameComparer) + ); + } else { + return base.GetFindMemberReferencesTextFinder(member); + } + } + + public override bool ShowMember(IMember member, bool showStatic) + { + if (!base.ShowMember(member, showStatic)) + return false; + // do not show 'Finalize' methods (they are not directly callable from C#) + IMethod method = member as IMethod; + if (method != null) { + if (method.Name == "Finalize" && method.Parameters.Count == 0) + return false; + } + return true; + } + + public override bool ShowMemberInOverrideCompletion(IMember member) + { + IMethod method = member as IMethod; + + if (method != null) { + if (method.Name == "Finalize" && method.Parameters.Count == 0) + return false; + } + + return base.ShowMemberInOverrideCompletion(member); + } + } + #endregion + + #region VBNetProperties + internal sealed class VBNetProperties : LanguageProperties + { + public VBNetProperties() : base(StringComparer.OrdinalIgnoreCase) {} + + public override bool ShowMember(IMember member, bool showStatic) + { + if (member is ArrayReturnType.ArrayIndexer) { + return false; + } + IMethod method = member as IMethod; + if (method != null && (method.IsConstructor || method.IsOperator)) { + return false; + } + return member.IsStatic || !showStatic; + } + + public override bool ImportNamespaces { + get { + return true; + } + } + + public override bool ImportModules { + get { + return true; + } + } + + public override bool CanImportClasses { + get { + return true; + } + } + + public override bool SupportsExtensionMethods { + get { return true; } + } + + public override bool SearchExtensionsInClasses { + get { return true; } + } + + public override bool IsClassWithImplicitlyStaticMembers(IClass c) + { + return c.ClassType == ClassType.Module; + } + + public override bool ShowInNamespaceCompletion(IClass c) + { + foreach (IAttribute attr in c.Attributes) { + if (attr.AttributeType.FullyQualifiedName == "Microsoft.VisualBasic.HideModuleNameAttribute") + return false; + } + return base.ShowInNamespaceCompletion(c); + } + + public override IUsing CreateDefaultImports(IProjectContent pc) + { + DefaultUsing u = new DefaultUsing(pc); + u.Usings.Add("Microsoft.VisualBasic"); + u.Usings.Add("System"); + u.Usings.Add("System.Collections"); + u.Usings.Add("System.Collections.Generic"); + u.Usings.Add("System.Drawing"); + u.Usings.Add("System.Diagnostics"); + u.Usings.Add("System.Windows.Forms"); + return u; + } + + public override RefactoringProvider RefactoringProvider { + get { + return NRefactoryRefactoringProvider.NRefactoryVBNetProviderInstance; + } + } + + public override CodeGenerator CodeGenerator { + get { + return VBNetCodeGenerator.Instance; + } + } + + public override System.CodeDom.Compiler.CodeDomProvider CodeDomProvider { + get { + return new Microsoft.VisualBasic.VBCodeProvider(); + } + } + + public override IAmbience GetAmbience() + { + return new VBNet.VBNetAmbience(); + } + + public override string IndexerExpressionStartToken { + get { return "("; } + } + + public override string ToString() + { + return "[LanguageProperties: VB.NET]"; + } + } + #endregion + + #region Text Finder + protected sealed class WholeWordTextFinder : TextFinder + { + readonly string searchedText; + readonly bool caseInsensitive; + + public WholeWordTextFinder(string word, StringComparer nameComparer) + { + if (word == null) word = string.Empty; + + caseInsensitive = nameComparer.Equals("a", "A"); + if (caseInsensitive) + this.searchedText = word.ToLowerInvariant(); + else + this.searchedText = word; + } + + public override string PrepareInputText(string inputText) + { + if (caseInsensitive) + return inputText.ToLowerInvariant(); + else + return inputText; + } + + public override TextFinderMatch Find(string inputText, int startPosition) + { + if (searchedText.Length == 0) + return TextFinderMatch.Empty; + int pos = startPosition - 1; + while ((pos = inputText.IndexOf(searchedText, pos + 1)) >= 0) { + if (pos > 0 && char.IsLetterOrDigit(inputText, pos - 1)) { + continue; // memberName is not a whole word (a.SomeName cannot reference Name) + } + if (pos < inputText.Length - searchedText.Length - 1 + && char.IsLetterOrDigit(inputText, pos + searchedText.Length)) + { + continue; // memberName is not a whole word (a.Name2 cannot reference Name) + } + return new TextFinderMatch(pos, searchedText.Length); + } + return TextFinderMatch.Empty; + } + } + + protected sealed class CombinedTextFinder : TextFinder + { + readonly TextFinder[] finders; + + public CombinedTextFinder(params TextFinder[] finders) + { + if (finders == null) + throw new ArgumentNullException("finders"); + if (finders.Length == 0) + throw new ArgumentException("finders.Length must be > 0"); + this.finders = finders; + } + + public override string PrepareInputText(string inputText) + { + return finders[0].PrepareInputText(inputText); + } + + public override TextFinderMatch Find(string inputText, int startPosition) + { + TextFinderMatch best = TextFinderMatch.Empty; + foreach (TextFinder f in finders) { + TextFinderMatch r = f.Find(inputText, startPosition); + if (r.Position >= 0 && (best.Position < 0 || r.Position < best.Position)) { + best = r; + } + } + return best; + } + } + + protected sealed class IndexBeforeTextFinder : TextFinder + { + readonly string searchText; + + public IndexBeforeTextFinder(string searchText) + { + this.searchText = searchText; + } + + public override TextFinderMatch Find(string inputText, int startPosition) + { + int pos = inputText.IndexOf(searchText, startPosition); + if (pos >= 0) { + return new TextFinderMatch(pos, searchText.Length, pos - 1); + } else { + return TextFinderMatch.Empty; + } + } + } + #endregion + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/LazyList.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/LazyList.cs new file mode 100644 index 000000000..955e30310 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/LazyList.cs @@ -0,0 +1,95 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// A list that lazily initializes its content. The base list returned by the initializer may be + /// modified. + /// + sealed class LazyList : IList + { + readonly Func> initializer; + IList innerList; + + public IList InnerList { + get { + if (innerList == null) + innerList = initializer(); + return innerList; + } + } + + public LazyList(Func> initializer) + { + if (initializer == null) throw new ArgumentNullException("initializer"); + this.initializer = initializer; + } + + public T this[int index] { + get { return InnerList[index]; } + set { InnerList[index] = value; } + } + + public int Count { + get { return InnerList.Count; } + } + + public bool IsReadOnly { + get { return InnerList.IsReadOnly; } + } + + public int IndexOf(T item) + { + return InnerList.IndexOf(item); + } + + public void Insert(int index, T item) + { + InnerList.Insert(index, item); + } + + public void RemoveAt(int index) + { + InnerList.RemoveAt(index); + } + + public void Add(T item) + { + InnerList.Add(item); + } + + public void Clear() + { + InnerList.Clear(); + } + + public bool Contains(T item) + { + return InnerList.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + InnerList.CopyTo(array, arrayIndex); + } + + public bool Remove(T item) + { + return InnerList.Remove(item); + } + + public IEnumerator GetEnumerator() + { + return InnerList.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/LoggingService.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/LoggingService.cs new file mode 100644 index 000000000..4f141e7e4 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/LoggingService.cs @@ -0,0 +1,52 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using log4net; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// We don't reference ICSharpCode.Core but still need the logging interface. + /// + internal static class LoggingService + { + static ILog log = LogManager.GetLogger(typeof(LoggingService)); + + public static void Debug(object message) + { + log.Debug(message); + } + + public static void Info(object message) + { + log.Info(message); + } + + public static void Warn(object message) + { + log.Warn(message); + } + + public static void Warn(object message, Exception exception) + { + log.Warn(message, exception); + } + + public static void Error(object message) + { + log.Error(message); + } + + public static void Error(object message, Exception exception) + { + log.Error(message, exception); + } + + public static bool IsDebugEnabled { + get { + return log.IsDebugEnabled; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs new file mode 100644 index 000000000..a6d04977c --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs @@ -0,0 +1,871 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Class with methods to help finding the correct overload for a member. + /// + /// + /// This class does member lookup as specified by the C# spec (ECMA-334, § 14.3). + /// Other languages might need custom lookup methods. + /// + public static class MemberLookupHelper + { + #region LookupMember / GetAccessibleMembers + public static List GetAllMembers(IReturnType rt) + { + List members = new List(); + if (rt != null) { + rt.GetMethods().ForEach(members.Add); + rt.GetProperties().ForEach(members.Add); + rt.GetFields().ForEach(members.Add); + rt.GetEvents().ForEach(members.Add); + } + return members; + } + + public static List> LookupMember( + IReturnType type, string name, IClass callingClass, + LanguageProperties language, bool isInvocation, bool? isAccessThoughReferenceOfCurrentClass) + { + if (language == null) + throw new ArgumentNullException("language"); + + if (isAccessThoughReferenceOfCurrentClass == null) { + isAccessThoughReferenceOfCurrentClass = false; + IClass underlyingClass = type.GetUnderlyingClass(); + if (underlyingClass != null) + isAccessThoughReferenceOfCurrentClass = underlyingClass.IsTypeInInheritanceTree(callingClass); + } + + IEnumerable members; + if (language == LanguageProperties.VBNet && language.NameComparer.Equals(name, "New")) { + members = GetAllMembers(type).OfType().Where(m => m.IsConstructor).Select(m=>(IMember)m); + } else { + members = GetAllMembers(type).Where(m => language.NameComparer.Equals(m.Name, name)); + } + + return LookupMember(members, callingClass, (bool)isAccessThoughReferenceOfCurrentClass, isInvocation); + } + + sealed class InheritanceLevelComparer : IComparer + { + public readonly static InheritanceLevelComparer Instance = new InheritanceLevelComparer(); + + public int Compare(IClass x, IClass y) + { + if (x == y) + return 0; + if (x.IsTypeInInheritanceTree(y)) + return 1; + else + return -1; + } + } + + public static List> LookupMember( + IEnumerable possibleMembers, IClass callingClass, + bool isAccessThoughReferenceOfCurrentClass, bool isInvocation) + { +// Console.WriteLine("Possible members:"); +// foreach (IMember m in possibleMembers) { +// Console.WriteLine(" " + m.DotNetName); +// } + + IEnumerable accessibleMembers = possibleMembers.Where(member => member.IsAccessible(callingClass, isAccessThoughReferenceOfCurrentClass)); + if (isInvocation) { + accessibleMembers = accessibleMembers.Where(IsInvocable); + } + + // base most member => most derived member + //Dictionary overrideDict = new Dictionary(); + + ParameterListComparer parameterListComparer = new ParameterListComparer(); + HashSet handledMethods = new HashSet(parameterListComparer); + Dictionary overrideMethodDict = new Dictionary(parameterListComparer); + IMember nonMethodOverride = null; + + List> allResults = new List>(); + List results = new List(); + + foreach (var group in accessibleMembers + .GroupBy(m => m.DeclaringType.GetCompoundClass()) + .OrderByDescending(g => g.Key, InheritanceLevelComparer.Instance)) + { + //Console.WriteLine("Member group " + group.Key); + foreach (IMember m in group) { + //Console.WriteLine(" " + m.DotNetName); + if (m.IsOverride) { + IMethod method = m as IMethod; + if (method != null) { + if (!overrideMethodDict.ContainsKey(method)) + overrideMethodDict[method] = method; + } else { + if (nonMethodOverride == null) + nonMethodOverride = m; + } + } else { + IMethod method = m as IMethod; + if (method != null) { + if (handledMethods.Add(method)) { + IMethod mostOverriddenMethod; + if (overrideMethodDict.TryGetValue(method, out mostOverriddenMethod)) + results.Add(mostOverriddenMethod); + else { + results.Add(method); + } + } + } else { + // non-methods are only available if they aren't hidden by something else + if (allResults.Count == 0) { + results.Add(nonMethodOverride ?? m); + } + } + } + } + if (results.Count > 0) { + allResults.Add(results); + results = new List(); + } + } + // Sometimes there might be 'override's without corresponding 'virtual's. + // Ensure those get found, too. + if (nonMethodOverride != null && allResults.Count == 0) { + results.Add(nonMethodOverride); + } + foreach (IMethod method in overrideMethodDict.Values) { + if (handledMethods.Add(method)) { + results.Add(method); + } + } + if (results.Count > 0) { + allResults.Add(results); + } + return allResults; + } + + static bool IsInvocable(IMember member) + { + if (member == null) + throw new ArgumentNullException("member"); + if (member is IMethod || member is IEvent) + return true; + IProperty p = member as IProperty; + if (p != null && p.Parameters.Count > 0) + return true; + IReturnType returnType = member.ReturnType; + if (returnType == null) + return false; + IClass c = returnType.GetUnderlyingClass(); + return c != null && c.ClassType == ClassType.Delegate; + } + + /// + /// Gets all accessible members, including indexers and constructors. + /// + public static List GetAccessibleMembers(IReturnType rt, IClass callingClass, LanguageProperties language) + { + bool isAccessThoughReferenceOfCurrentClass = false; + IClass underlyingClass = rt.GetUnderlyingClass(); + if (underlyingClass != null) + isAccessThoughReferenceOfCurrentClass = underlyingClass.IsTypeInInheritanceTree(callingClass); + return GetAccessibleMembers(rt, callingClass, language, isAccessThoughReferenceOfCurrentClass); + } + + /// + /// Gets all accessible members, including indexers and constructors. + /// + public static List GetAccessibleMembers(IReturnType rt, IClass callingClass, LanguageProperties language, bool isAccessThoughReferenceOfCurrentClass) + { + if (language == null) + throw new ArgumentNullException("language"); + + List result = new List(); + foreach (var g in GetAllMembers(rt).GroupBy(m => m.Name, language.NameComparer).OrderBy(g2=>g2.Key)) { + foreach (var group in LookupMember(g, callingClass, isAccessThoughReferenceOfCurrentClass, false)) { + result.AddRange(group); + } + } + return result; + } + #endregion + + #region FindOverload + /// + /// Finds the correct overload according to the C# specification. + /// + /// List with the methods to check. + /// The types of the arguments passed to the method. + /// Out parameter. Will be true if the resulting method + /// is an acceptable match, false if the resulting method is just a guess and will lead + /// to a compile error. + /// The method that will be called. + public static IMethod FindOverload(IList methods, IReturnType[] arguments, out bool resultIsAcceptable) + { + if (methods == null) + throw new ArgumentNullException("methods"); + resultIsAcceptable = false; + if (methods.Count == 0) + return null; + return (IMethod)CSharp.OverloadResolution.FindOverload( + methods, + arguments, + false, + true, + out resultIsAcceptable); + } + + public static IProperty FindOverload(IList properties, IReturnType[] arguments) + { + if (properties.Count == 0) + return null; + bool acceptableMatch; + return (IProperty)CSharp.OverloadResolution.FindOverload( + properties, + arguments, + false, + false, + out acceptableMatch); + } + #endregion + + #region Type Argument Inference + /// + /// Infers type arguments specified by passing expectedArgument as parameter where passedArgument + /// was expected. The resulting type arguments are written to outputArray. + /// Returns false when expectedArgument and passedArgument are incompatible, otherwise true + /// is returned (true is used both for successful inferring and other kind of errors). + /// + /// Warning: This method for single-argument type inference doesn't support lambdas! + /// + /// + /// The C# spec (§ 25.6.4) has a bug: it says that type inference works if the passedArgument is IEnumerable{T} + /// and the expectedArgument is an array; passedArgument and expectedArgument must be swapped here. + /// + public static bool InferTypeArgument(IReturnType expectedArgument, IReturnType passedArgument, IReturnType[] outputArray) + { + if (expectedArgument == null) return true; + if (passedArgument == null || passedArgument == NullReturnType.Instance) return true; + + if (passedArgument.IsArrayReturnType) { + IReturnType passedArrayElementType = passedArgument.CastToArrayReturnType().ArrayElementType; + if (expectedArgument.IsArrayReturnType && expectedArgument.CastToArrayReturnType().ArrayDimensions == passedArgument.CastToArrayReturnType().ArrayDimensions) { + return InferTypeArgument(expectedArgument.CastToArrayReturnType().ArrayElementType, passedArrayElementType, outputArray); + } else if (expectedArgument.IsConstructedReturnType) { + switch (expectedArgument.FullyQualifiedName) { + case "System.Collections.Generic.IList": + case "System.Collections.Generic.ICollection": + case "System.Collections.Generic.IEnumerable": + return InferTypeArgument(expectedArgument.CastToConstructedReturnType().TypeArguments[0], passedArrayElementType, outputArray); + } + } + // If P is an array type, and A is not an array type of the same rank, + // or an instantiation of IList<>, ICollection<>, or IEnumerable<>, then + // type inference fails for the generic method. + return false; + } + if (expectedArgument.IsGenericReturnType) { + GenericReturnType methodTP = expectedArgument.CastToGenericReturnType(); + if (methodTP.TypeParameter.Method != null) { + if (methodTP.TypeParameter.Index < outputArray.Length) { + outputArray[methodTP.TypeParameter.Index] = passedArgument; + } + return true; + } + } + if (expectedArgument.IsConstructedReturnType) { + // The spec for this case is quite complex. + // For our purposes, we can simplify enourmously: + if (!passedArgument.IsConstructedReturnType) return false; + + IList expectedTA = expectedArgument.CastToConstructedReturnType().TypeArguments; + IList passedTA = passedArgument.CastToConstructedReturnType().TypeArguments; + + int count = Math.Min(expectedTA.Count, passedTA.Count); + for (int i = 0; i < count; i++) { + InferTypeArgument(expectedTA[i], passedTA[i], outputArray); + } + } + return true; + } + #endregion + + #region IsApplicable + public static bool IsApplicable(IReturnType argument, IParameter expected, IMethod targetMethod) + { + bool parameterIsRefOrOut = expected.IsRef || expected.IsOut; + bool argumentIsRefOrOut = argument != null && argument.IsDecoratingReturnType(); + if (parameterIsRefOrOut != argumentIsRefOrOut) + return false; + if (parameterIsRefOrOut) { + return object.Equals(argument, expected.ReturnType); + } else { + return IsApplicable(argument, expected.ReturnType, targetMethod); + } + } + + /// + /// Tests whether an argument of type "argument" is valid for a parameter of type "expected" for a call + /// to "targetMethod". + /// targetMethod may be null, it is only used when it is a generic method and expected is (or contains) one of + /// its type parameters. + /// + public static bool IsApplicable(IReturnType argument, IReturnType expected, IMethod targetMethod) + { + return ConversionExistsInternal(argument, expected, targetMethod); + } + #endregion + + #region Conversion exists + /// + /// Checks if an implicit conversion exists from to . + /// + public static bool ConversionExists(IReturnType from, IReturnType to) + { + return ConversionExistsInternal(from, to, null); + } + + /// + /// Tests if an implicit conversion exists from "from" to "to". + /// Conversions from concrete types to generic types are only allowed when the generic type belongs to the + /// method "allowGenericTargetsOnThisMethod". + /// + static bool ConversionExistsInternal(IReturnType from, IReturnType to, IMethod allowGenericTargetsOnThisMethod) + { + // ECMA-334, § 13.1 Implicit conversions + + // Identity conversion: + if (from == to) return true; + if (from == null || to == null) return false; + if (from.Equals(to)) { + return true; + } + + bool fromIsDefault = from.IsDefaultReturnType; + bool toIsDefault = to.IsDefaultReturnType; + + if (fromIsDefault && toIsDefault) { + // Implicit numeric conversions: + int f = GetPrimitiveType(from); + int t = GetPrimitiveType(to); + if (f == SByte && (t == Short || t == Int || t == Long || t == Float || t == Double || t == Decimal)) + return true; + if (f == Byte && (t == Short || t == UShort || t == Int || t == UInt || t == Long || t == ULong || t == Float || t == Double || t == Decimal)) + return true; + if (f == Short && (t == Int || t == Long || t == Float || t == Double || t == Decimal)) + return true; + if (f == UShort && (t == Int || t == UInt || t == Long || t == ULong || t == Float || t == Double || t == Decimal)) + return true; + if (f == Int && (t == Long || t == Float || t == Double || t == Decimal)) + return true; + if (f == UInt && (t == Long || t == ULong || t == Float || t == Double || t == Decimal)) + return true; + if ((f == Long || f == ULong) && (t == Float || t == Double || t == Decimal)) + return true; + if (f == Char && (t == UShort || t == Int || t == UInt || t == Long || t == ULong || t == Float || t == Double || t == Decimal)) + return true; + if (f == Float && t == Double) + return true; + } + // Implicit reference conversions: + + if (toIsDefault && to.FullyQualifiedName == "System.Object") { + return true; // from any type to object + } + if (from == NullReturnType.Instance) { + IClass toClass = to.GetUnderlyingClass(); + if (toClass != null) { + switch (toClass.ClassType) { + case ClassType.Class: + case ClassType.Delegate: + case ClassType.Interface: + return true; + case ClassType.Struct: + return toClass.FullyQualifiedName == "System.Nullable"; + } + } + return false; + } + + if ((toIsDefault || to.IsConstructedReturnType || to.IsGenericReturnType) + && (fromIsDefault || from.IsArrayReturnType || from.IsConstructedReturnType)) + { + foreach (IReturnType baseTypeOfFrom in GetTypeInheritanceTree(from)) { + if (IsConstructedConversionToGenericReturnType(baseTypeOfFrom, to, allowGenericTargetsOnThisMethod)) + return true; + } + } + + if (from.IsArrayReturnType && to.IsArrayReturnType) { + ArrayReturnType fromArt = from.CastToArrayReturnType(); + ArrayReturnType toArt = to.CastToArrayReturnType(); + // from array to other array type + if (fromArt.ArrayDimensions == toArt.ArrayDimensions) { + return ConversionExistsInternal(fromArt.ArrayElementType, toArt.ArrayElementType, allowGenericTargetsOnThisMethod); + } + } + + if (from.IsDecoratingReturnType() && (toIsDefault || to.IsConstructedReturnType)) { + AnonymousMethodReturnType amrt = from.CastToDecoratingReturnType(); + IMethod method = CSharp.TypeInference.GetDelegateOrExpressionTreeSignature(to, amrt.CanBeConvertedToExpressionTree); + if (method != null) { + if (amrt.HasParameterList) { + if (amrt.MethodParameters.Count != method.Parameters.Count) + return false; + for (int i = 0; i < amrt.MethodParameters.Count; i++) { + if (amrt.MethodParameters[i].ReturnType != null) { + if (!object.Equals(amrt.MethodParameters[i].ReturnType, + method.Parameters[i].ReturnType)) + { + return false; + } + } + } + } + IReturnType rt = amrt.ResolveReturnType(method.Parameters.Select(p => p.ReturnType).ToArray()); + return ConversionExistsInternal(rt, method.ReturnType, allowGenericTargetsOnThisMethod); + } + } + + return false; + } + + static bool IsConstructedConversionToGenericReturnType(IReturnType from, IReturnType to, IMethod allowGenericTargetsOnThisMethod) + { + // null could be passed when type arguments could not be resolved/inferred + if (from == to) // both are null or + return true; + if (from == null || to == null) + return false; + + if (from.Equals(to)) + return true; + + if (allowGenericTargetsOnThisMethod == null) + return false; + + if (to.IsGenericReturnType) { + ITypeParameter typeParameter = to.CastToGenericReturnType().TypeParameter; + if (typeParameter.Method == allowGenericTargetsOnThisMethod) + return true; + // applicability ignores constraints +// foreach (IReturnType constraintType in typeParameter.Constraints) { +// if (!ConversionExistsInternal(from, constraintType, allowGenericTargetsOnThisMethod)) { +// return false; +// } +// } + return false; + } + + // for conversions like from IEnumerable to IEnumerable, where T is a GenericReturnType + ConstructedReturnType cFrom = from.CastToConstructedReturnType(); + ConstructedReturnType cTo = to.CastToConstructedReturnType(); + if (cFrom != null && cTo != null) { + if (cFrom.FullyQualifiedName == cTo.FullyQualifiedName && cFrom.TypeArguments.Count == cTo.TypeArguments.Count) { + for (int i = 0; i < cFrom.TypeArguments.Count; i++) { + if (!IsConstructedConversionToGenericReturnType(cFrom.TypeArguments[i], cTo.TypeArguments[i], allowGenericTargetsOnThisMethod)) + return false; + } + return true; + } + } + return false; + } + #endregion + + #region Better conversion + /// + /// Gets if the conversion from to is better than + /// the conversion from to . + /// + /// + /// 0 = neither conversion is better
+ /// 1 = from -> to1 is the better conversion
+ /// 2 = from -> to2 is the better conversion. + ///
+ public static int GetBetterConversion(IReturnType from, IReturnType to1, IReturnType to2) + { + if (from == null) return 0; + if (to1 == null) return 2; + if (to2 == null) return 1; + + // See ECMA-334, § 14.4.2.3 + + // If T1 and T2 are the same type, neither conversion is better. + if (to1.Equals(to2)) { + return 0; + } + // If S is T1, C1 is the better conversion. + if (from.Equals(to1)) { + return 1; + } + // If S is T2, C2 is the better conversion. + if (from.Equals(to2)) { + return 2; + } + bool canConvertFrom1To2 = ConversionExists(to1, to2); + bool canConvertFrom2To1 = ConversionExists(to2, to1); + // If an implicit conversion from T1 to T2 exists, and no implicit conversion + // from T2 to T1 exists, C1 is the better conversion. + if (canConvertFrom1To2 && !canConvertFrom2To1) { + return 1; + } + // If an implicit conversion from T2 to T1 exists, and no implicit conversion + // from T1 to T2 exists, C2 is the better conversion. + if (canConvertFrom2To1 && !canConvertFrom1To2) { + return 2; + } + if (to1.IsDefaultReturnType && to2.IsDefaultReturnType) { + return GetBetterPrimitiveConversion(to1, to2); + } + // Otherwise, neither conversion is better. + return 0; + } + + const int Byte = 1; + const int Short = 2; + const int Int = 3; + const int Long = 4; + const int SByte = 5; + const int UShort = 6; + const int UInt = 7; + const int ULong = 8; + const int Float = 9; + const int Double = 10; + const int Char = 11; + const int Decimal= 12; + + static int GetBetterPrimitiveConversion(IReturnType to1, IReturnType to2) + { + int t1 = GetPrimitiveType(to1); + int t2 = GetPrimitiveType(to2); + if (t1 == 0 || t2 == 0) return 0; // not primitive + if (t1 == SByte && (t2 == Byte || t2 == UShort || t2 == UInt || t2 == ULong)) + return 1; + if (t2 == SByte && (t1 == Byte || t1 == UShort || t1 == UInt || t1 == ULong)) + return 2; + if (t1 == Short && (t2 == UShort || t2 == UInt || t2 == ULong)) + return 1; + if (t2 == Short && (t1 == UShort || t1 == UInt || t1 == ULong)) + return 2; + if (t1 == Int && (t2 == UInt || t2 == ULong)) + return 1; + if (t2 == Int && (t1 == UInt || t1 == ULong)) + return 2; + if (t1 == Long && t2 == ULong) + return 1; + if (t2 == Long && t1 == ULong) + return 2; + return 0; + } + + static int GetPrimitiveType(IReturnType t) + { + switch (t.FullyQualifiedName) { + case "System.SByte": return SByte; + case "System.Byte": return Byte; + case "System.Int16": return Short; + case "System.UInt16": return UShort; + case "System.Int32": return Int; + case "System.UInt32": return UInt; + case "System.Int64": return Long; + case "System.UInt64": return ULong; + case "System.Single": return Float; + case "System.Double": return Double; + case "System.Char": return Char; + case "System.Decimal": return Decimal; + default: return 0; + } + } + #endregion + + #region GetCommonType + /// + /// Gets the common base type of a and b. + /// + public static IReturnType GetCommonType(IProjectContent projectContent, IReturnType a, IReturnType b) + { + if (projectContent == null) + throw new ArgumentNullException("projectContent"); + if (a == null) return b; + if (b == null) return a; + if (ConversionExists(a, b)) + return b; + //if (ConversionExists(b, a)) - not required because the first baseTypeOfA is a + // return a; + foreach (IReturnType baseTypeOfA in GetTypeInheritanceTree(a)) { + if (ConversionExists(b, baseTypeOfA)) + return baseTypeOfA; + } + return projectContent.SystemTypes.Object; + } + #endregion + + #region GetTypeParameterPassedToBaseClass / GetTypeInheritanceTree + /// + /// Gets the type parameter that was passed to a certain base class. + /// For example, when is Dictionary(of string, int) + /// this method will return KeyValuePair(of string, int) + /// + public static IReturnType GetTypeParameterPassedToBaseClass(IReturnType parentType, IClass baseClass, int baseClassTypeParameterIndex) + { + foreach (IReturnType rt in GetTypeInheritanceTree(parentType)) { + ConstructedReturnType crt = rt.CastToConstructedReturnType(); + if (crt != null && baseClass.CompareTo(rt.GetUnderlyingClass()) == 0) { + if (baseClassTypeParameterIndex < crt.TypeArguments.Count) { + return crt.TypeArguments[baseClassTypeParameterIndex]; + } + } + } + return null; + } + + /// + /// Translates typeToTranslate using the type arguments from parentType; + /// + static IReturnType TranslateIfRequired(IReturnType parentType, IReturnType typeToTranslate) + { + if (typeToTranslate == null) + return null; + ConstructedReturnType parentConstructedType = parentType.CastToConstructedReturnType(); + if (parentConstructedType != null) { + return ConstructedReturnType.TranslateType(typeToTranslate, parentConstructedType.TypeArguments, false); + } else { + return typeToTranslate; + } + } + + readonly static Dictionary> getTypeInheritanceTreeCache = new Dictionary>(); + + static void ClearGetTypeInheritanceTreeCache() + { + lock (getTypeInheritanceTreeCache) { + getTypeInheritanceTreeCache.Clear(); + } + } + + /// + /// Gets all types the specified type inherits from (all classes and interfaces). + /// Unlike the class inheritance tree, this method takes care of type arguments and calculates the type + /// arguments that are passed to base classes. + /// + public static IEnumerable GetTypeInheritanceTree(IReturnType typeToListInheritanceTreeFor) + { + if (typeToListInheritanceTreeFor == null) + throw new ArgumentNullException("typeToListInheritanceTreeFor"); + + lock (getTypeInheritanceTreeCache) { + IEnumerable result; + if (getTypeInheritanceTreeCache.TryGetValue(typeToListInheritanceTreeFor, out result)) + return result; + } + + IClass classToListInheritanceTreeFor = typeToListInheritanceTreeFor.GetUnderlyingClass(); + if (classToListInheritanceTreeFor == null) + return new IReturnType[] { typeToListInheritanceTreeFor }; + + if (typeToListInheritanceTreeFor.IsArrayReturnType) { + IReturnType elementType = typeToListInheritanceTreeFor.CastToArrayReturnType().ArrayElementType; + List resultList = new List(); + resultList.Add(typeToListInheritanceTreeFor); + resultList.AddRange(GetTypeInheritanceTree( + new ConstructedReturnType( + classToListInheritanceTreeFor.ProjectContent.GetClass("System.Collections.Generic.IList", 1).DefaultReturnType, + new IReturnType[] { elementType } + ) + )); + resultList.Add(classToListInheritanceTreeFor.ProjectContent.GetClass("System.Collections.IList", 0).DefaultReturnType); + resultList.Add(classToListInheritanceTreeFor.ProjectContent.GetClass("System.Collections.ICollection", 0).DefaultReturnType); + // non-generic IEnumerable is already added by generic IEnumerable + return resultList; + } + + HashSet visitedSet = new HashSet(); + List visitedList = new List(); + Queue typesToVisit = new Queue(); + bool enqueuedLastBaseType = false; + + IReturnType currentType = typeToListInheritanceTreeFor; + IClass currentClass = classToListInheritanceTreeFor; + IReturnType nextType; + do { + if (currentClass != null) { + if (visitedSet.Add(currentType)) { + visitedList.Add(currentType); + foreach (IReturnType type in currentClass.BaseTypes) { + typesToVisit.Enqueue(TranslateIfRequired(currentType, type)); + } + } + } + if (typesToVisit.Count > 0) { + nextType = typesToVisit.Dequeue(); + } else { + nextType = enqueuedLastBaseType ? null : DefaultClass.GetBaseTypeByClassType(classToListInheritanceTreeFor); + enqueuedLastBaseType = true; + } + if (nextType != null) { + currentType = nextType; + currentClass = nextType.GetUnderlyingClass(); + } + } while (nextType != null); + lock (getTypeInheritanceTreeCache) { + if (getTypeInheritanceTreeCache.Count == 0) { + DomCache.RegisterForClear(ClearGetTypeInheritanceTreeCache); + } + getTypeInheritanceTreeCache[typeToListInheritanceTreeFor] = visitedList; + } + return visitedList; + } + #endregion + + #region IsSimilarMember / FindBaseMember + /// + /// Gets if member1 is the same as member2 or if member1 overrides member2. + /// + public static bool IsSimilarMember(IMember member1, IMember member2) + { + member1 = GetGenericMember(member1); + member2 = GetGenericMember(member2); + do { + if (IsSimilarMemberInternal(member1, member2)) + return true; + } while ((member1 = FindBaseMember(member1)) != null); + return false; + } + + /// + /// Gets the generic member from a specialized member. + /// Specialized members are the result of overload resolution with type substitution. + /// + static IMember GetGenericMember(IMember member) + { + // e.g. member = string[] ToArray(IEnumerable input) + // result = T[] ToArray(IEnumerable input) + if (member != null) { + while (member.GenericMember != null) + member = member.GenericMember; + } + return member; + } + + static bool IsSimilarMemberInternal(IMember member1, IMember member2) + { + if (member1 == member2) + return true; + if (member1 == null || member2 == null) + return false; + if (member1.FullyQualifiedName != member2.FullyQualifiedName) + return false; + if (member1.IsStatic != member2.IsStatic) + return false; + IMethodOrProperty m1 = member1 as IMethodOrProperty; + IMethodOrProperty m2 = member2 as IMethodOrProperty; + if (m1 != null || m2 != null) { + if (m1 != null && m2 != null) { + if (DiffUtility.Compare(m1.Parameters, m2.Parameters) != 0) + return false; + if (m1 is IMethod && m2 is IMethod) { + if ((m1 as IMethod).TypeParameters.Count != (m2 as IMethod).TypeParameters.Count) + return false; + } + } else { + return false; + } + } + IField f1 = member1 as IField; + IField f2 = member2 as IField; + if (f1 != null || f2 != null) { + if (f1 != null && f2 != null) { + if (f1.IsLocalVariable != f2.IsLocalVariable || f1.IsParameter != f2.IsParameter) + return false; + } else { + return false; + } + } + return true; + } + + public static IMember FindSimilarMember(IClass type, IMember member) + { + if (type == null) + throw new ArgumentNullException("type"); + StringComparer nameComparer = member.DeclaringType.ProjectContent.Language.NameComparer; + member = GetGenericMember(member); + if (member is IMethod) { + IMethod parentMethod = (IMethod)member; + foreach (IMethod m in type.Methods) { + if (nameComparer.Equals(parentMethod.Name, m.Name)) { + if (m.IsStatic == parentMethod.IsStatic) { + if (DiffUtility.Compare(parentMethod.Parameters, m.Parameters) == 0) { + return m; + } + } + } + } + } else if (member is IProperty) { + IProperty parentMethod = (IProperty)member; + foreach (IProperty m in type.Properties) { + if (nameComparer.Equals(parentMethod.Name, m.Name)) { + if (m.IsStatic == parentMethod.IsStatic) { + if (DiffUtility.Compare(parentMethod.Parameters, m.Parameters) == 0) { + return m; + } + } + } + } + } + return null; + } + + public static IMember FindBaseMember(IMember member) + { + if (member == null) return null; + if (member is IMethod && (member as IMethod).IsConstructor) return null; + IClass parentClass = member.DeclaringType; + IClass baseClass = parentClass.BaseClass; + if (baseClass == null) return null; + + foreach (IClass childClass in baseClass.ClassInheritanceTree) { + IMember m = FindSimilarMember(childClass, member); + if (m != null) + return m; + } + return null; + } + #endregion + + [System.Diagnostics.ConditionalAttribute("DEBUG")] + internal static void Log(string text) + { + Debug.WriteLine(text); + } + + [System.Diagnostics.ConditionalAttribute("DEBUG")] + internal static void Log(string text, IEnumerable types) + { + Log(text, types.Select(t => t != null ? t.DotNetName : "")); + } + + [System.Diagnostics.ConditionalAttribute("DEBUG")] + internal static void Log(string text, IEnumerable lines) + { + #if DEBUG + T[] arr = lines.ToArray(); + if (arr.Length == 0) { + Log(text + ""); + } else { + Log(text + arr[0]); + for (int i = 1; i < arr.Length; i++) { + Log(new string(' ', text.Length) + arr[i]); + } + } + #endif + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CSharpToVBNetConvertVisitor.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CSharpToVBNetConvertVisitor.cs new file mode 100644 index 000000000..afd2871da --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CSharpToVBNetConvertVisitor.cs @@ -0,0 +1,546 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Linq; +using System.Collections.Generic; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.AstBuilder; +using ICSharpCode.NRefactory.Visitors; +using System.Runtime.InteropServices; + +namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver +{ + /// + /// This class converts C# constructs to their VB.NET equivalents. + /// + public class CSharpToVBNetConvertVisitor : CSharpConstructsConvertVisitor + { + NRefactoryResolver resolver; + ParseInformation parseInformation; + IProjectContent projectContent; + public string RootNamespaceToRemove { get; set; } + public string StartupObjectToMakePublic { get; set; } + public IList DefaultImportsToRemove { get; set; } + + public CSharpToVBNetConvertVisitor(IProjectContent pc, ParseInformation parseInfo) + { + this.resolver = new NRefactoryResolver(LanguageProperties.CSharp); + this.projectContent = pc; + this.parseInformation = parseInfo; + } + + public override object VisitCompilationUnit(CompilationUnit compilationUnit, object data) + { + base.VisitCompilationUnit(compilationUnit, data); + ToVBNetConvertVisitor v = new ToVBNetConvertVisitor(); + compilationUnit.AcceptVisitor(v, data); + return null; + } + + IReturnType ResolveType(TypeReference typeRef) + { + return TypeVisitor.CreateReturnType(typeRef, resolver); + } + + public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data) + { + if (RootNamespaceToRemove != null) { + if (namespaceDeclaration.Name == RootNamespaceToRemove) { + // remove namespace declaration + INode insertAfter = namespaceDeclaration; + foreach (INode child in namespaceDeclaration.Children) { + InsertAfterSibling(insertAfter, child); + insertAfter = child; + } + namespaceDeclaration.Children.Clear(); + RemoveCurrentNode(); + } else if (namespaceDeclaration.Name.StartsWith(RootNamespaceToRemove + ".")) { + namespaceDeclaration.Name = namespaceDeclaration.Name.Substring(RootNamespaceToRemove.Length + 1); + } + } + base.VisitNamespaceDeclaration(namespaceDeclaration, data); + return null; + } + + public override object VisitUsing(Using @using, object data) + { + base.VisitUsing(@using, data); + if (DefaultImportsToRemove != null && !@using.IsAlias) { + if (DefaultImportsToRemove.Contains(@using.Name)) { + RemoveCurrentNode(); + } + } + return null; + } + + public override object VisitUsingDeclaration(UsingDeclaration usingDeclaration, object data) + { + base.VisitUsingDeclaration(usingDeclaration, data); + if (usingDeclaration.Usings.Count == 0) { + RemoveCurrentNode(); + } + return null; + } + + struct BaseType + { + internal readonly TypeReference TypeReference; + internal readonly IReturnType ReturnType; + internal readonly IClass UnderlyingClass; + + public BaseType(TypeReference typeReference, IReturnType returnType) + { + this.TypeReference = typeReference; + this.ReturnType = returnType; + this.UnderlyingClass = returnType.GetUnderlyingClass(); + } + } + + public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) + { + // Initialize resolver for method: + if (!methodDeclaration.Body.IsNull) { + if (resolver.Initialize(parseInformation, methodDeclaration.Body.StartLocation.Y, methodDeclaration.Body.StartLocation.X)) { + resolver.RunLookupTableVisitor(methodDeclaration); + } + } + IMethod currentMethod = resolver.CallingMember as IMethod; + CreateInterfaceImplementations(currentMethod, methodDeclaration, methodDeclaration.InterfaceImplementations); + // Make "Main" public + if (currentMethod != null && currentMethod.Name == "Main") { + if (currentMethod.DeclaringType.FullyQualifiedName == StartupObjectToMakePublic) { + if (currentMethod.IsStatic && currentMethod.IsPrivate) { + methodDeclaration.Modifier &= ~Modifiers.Private; + methodDeclaration.Modifier |= Modifiers.Internal; + } + } + } + if (resolver.CallingClass != null && resolver.CallingClass.BaseType != null) { + // methods with the same name as a method in a base class must have 'Overloads' + if ((methodDeclaration.Modifier & (Modifiers.Override | Modifiers.New)) == Modifiers.None) { + if (resolver.CallingClass.BaseType.GetMethods() + .Any(m => string.Equals(m.Name, methodDeclaration.Name, StringComparison.OrdinalIgnoreCase))) { + methodDeclaration.Modifier |= Modifiers.Overloads; + } + } + } + return base.VisitMethodDeclaration(methodDeclaration, data); + } + + ClassFinder CreateContext() + { + return new ClassFinder(resolver.CallingClass, resolver.CallingMember, resolver.CaretLine, resolver.CaretColumn); + } + + void CreateInterfaceImplementations(IMember currentMember, ParametrizedNode memberDecl, List interfaceImplementations) + { + if (currentMember != null + && (memberDecl.Modifier & Modifiers.Visibility) == Modifiers.None + && interfaceImplementations.Count == 1) + { + // member is explicitly implementing an interface member + // to convert explicit interface implementations to VB, make the member private + // and ensure its name does not collide with another member + memberDecl.Modifier |= Modifiers.Private; + memberDecl.Name = interfaceImplementations[0].InterfaceType.Type.Replace('.', '_') + "_" + memberDecl.Name; + } + + if (currentMember != null && currentMember.IsPublic + && currentMember.DeclaringType.ClassType != ClassType.Interface) + { + // member could be implicitly implementing an interface member, + // search for interfaces containing the member + foreach (IReturnType directBaseType in currentMember.DeclaringType.GetCompoundClass().BaseTypes) { + IClass directBaseClass = directBaseType.GetUnderlyingClass(); + if (directBaseClass != null && directBaseClass.ClassType == ClassType.Interface) { + // include members inherited from other interfaces in the search: + foreach (IReturnType baseType in MemberLookupHelper.GetTypeInheritanceTree(directBaseType)) { + IClass baseClass = baseType.GetUnderlyingClass(); + if (baseClass != null && baseClass.ClassType == ClassType.Interface) { + IMember similarMember = MemberLookupHelper.FindSimilarMember(baseClass, currentMember); + // add an interface implementation for similarMember + // only when similarMember is not explicitly implemented by another member in this class + if (similarMember != null && !HasExplicitImplementationFor(similarMember, baseType, memberDecl.Parent)) { + interfaceImplementations.Add(new InterfaceImplementation( + Refactoring.CodeGenerator.ConvertType(baseType, CreateContext()), + currentMember.Name)); + } + } + } + } + } + } + } + + bool HasExplicitImplementationFor(IMember interfaceMember, IReturnType interfaceReference, INode typeDecl) + { + if (typeDecl == null) + return false; + foreach (INode node in typeDecl.Children) { + MemberNode memberNode = node as MemberNode; + if (memberNode != null && memberNode.InterfaceImplementations.Count > 0) { + foreach (InterfaceImplementation impl in memberNode.InterfaceImplementations) { + if (impl.MemberName == interfaceMember.Name + && object.Equals(ResolveType(impl.InterfaceType), interfaceReference)) + { + return true; + } + } + } + } + return false; + } + + public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) + { + if (!constructorDeclaration.Body.IsNull) { + if (resolver.Initialize(parseInformation, constructorDeclaration.Body.StartLocation.Y, constructorDeclaration.Body.StartLocation.X)) { + resolver.RunLookupTableVisitor(constructorDeclaration); + } + } + return base.VisitConstructorDeclaration(constructorDeclaration, data); + } + + public override object VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data) + { + if (resolver.Initialize(parseInformation, propertyDeclaration.BodyStart.Y, propertyDeclaration.BodyStart.X)) { + resolver.RunLookupTableVisitor(propertyDeclaration); + } + IProperty currentProperty = resolver.CallingMember as IProperty; + CreateInterfaceImplementations(currentProperty, propertyDeclaration, propertyDeclaration.InterfaceImplementations); + return base.VisitPropertyDeclaration(propertyDeclaration, data); + } + + public override object VisitExpressionStatement(ExpressionStatement expressionStatement, object data) + { + if (resolver.CompilationUnit == null) + return base.VisitExpressionStatement(expressionStatement, data); + + // Transform event invocations that aren't already transformed by a parent IfStatement to RaiseEvent statement + InvocationExpression eventInvocation = expressionStatement.Expression as InvocationExpression; + if (eventInvocation != null && eventInvocation.TargetObject is IdentifierExpression) { + MemberResolveResult mrr = resolver.ResolveInternal(eventInvocation.TargetObject, ExpressionContext.Default) as MemberResolveResult; + if (mrr != null && mrr.ResolvedMember is IEvent) { + ReplaceCurrentNode(new RaiseEventStatement( + ((IdentifierExpression)eventInvocation.TargetObject).Identifier, + eventInvocation.Arguments)); + } + } + base.VisitExpressionStatement(expressionStatement, data); + + HandleAssignmentStatement(expressionStatement.Expression as AssignmentExpression); + return null; + } + + public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data) + { + base.VisitBinaryOperatorExpression(binaryOperatorExpression, data); + + if (resolver.CompilationUnit == null) + return null; + + switch (binaryOperatorExpression.Op) { + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + ConvertEqualityToReferenceEqualityIfRequired(binaryOperatorExpression); + break; + case BinaryOperatorType.Add: + ConvertArgumentsForStringConcatenationIfRequired(binaryOperatorExpression); + break; + case BinaryOperatorType.Divide: + ConvertDivisionToIntegerDivisionIfRequired(binaryOperatorExpression); + break; + } + return null; + } + + void ConvertEqualityToReferenceEqualityIfRequired(BinaryOperatorExpression binaryOperatorExpression) + { + // maybe we have to convert Equality operator to ReferenceEquality + ResolveResult left = resolver.ResolveInternal(binaryOperatorExpression.Left, ExpressionContext.Default); + ResolveResult right = resolver.ResolveInternal(binaryOperatorExpression.Right, ExpressionContext.Default); + if (left != null && right != null && left.ResolvedType != null && right.ResolvedType != null) { + IClass cLeft = left.ResolvedType.GetUnderlyingClass(); + IClass cRight = right.ResolvedType.GetUnderlyingClass(); + if (cLeft != null && cRight != null) { + if ((cLeft.ClassType != ClassType.Struct && cLeft.ClassType != ClassType.Enum) + || (cRight.ClassType != ClassType.Struct && cRight.ClassType != ClassType.Enum)) + { + // this is a reference comparison + if (cLeft.FullyQualifiedName != "System.String") { + // and it's not a string comparison, so we'll use reference equality + if (binaryOperatorExpression.Op == BinaryOperatorType.Equality) { + binaryOperatorExpression.Op = BinaryOperatorType.ReferenceEquality; + } else { + binaryOperatorExpression.Op = BinaryOperatorType.ReferenceInequality; + } + } + } + } + } + } + + void ConvertArgumentsForStringConcatenationIfRequired(BinaryOperatorExpression binaryOperatorExpression) + { + ResolveResult left = resolver.ResolveInternal(binaryOperatorExpression.Left, ExpressionContext.Default); + ResolveResult right = resolver.ResolveInternal(binaryOperatorExpression.Right, ExpressionContext.Default); + + if (left != null && right != null) { + if (IsString(left.ResolvedType)) { + binaryOperatorExpression.Op = BinaryOperatorType.Concat; + if (NeedsExplicitConversionToString(right.ResolvedType)) { + binaryOperatorExpression.Right = CreateExplicitConversionToString(binaryOperatorExpression.Right); + } + } else if (IsString(right.ResolvedType)) { + binaryOperatorExpression.Op = BinaryOperatorType.Concat; + if (NeedsExplicitConversionToString(left.ResolvedType)) { + binaryOperatorExpression.Left = CreateExplicitConversionToString(binaryOperatorExpression.Left); + } + } + } + } + + void ConvertDivisionToIntegerDivisionIfRequired(BinaryOperatorExpression binaryOperatorExpression) + { + ResolveResult left = resolver.ResolveInternal(binaryOperatorExpression.Left, ExpressionContext.Default); + ResolveResult right = resolver.ResolveInternal(binaryOperatorExpression.Right, ExpressionContext.Default); + + if (left != null && right != null) { + if (IsInteger(left.ResolvedType) && IsInteger(right.ResolvedType)) { + binaryOperatorExpression.Op = BinaryOperatorType.DivideInteger; + } + } + } + + bool IsString(IReturnType rt) + { + return rt != null && rt.IsDefaultReturnType && rt.FullyQualifiedName == "System.String"; + } + + bool IsInteger(IReturnType rt) + { + if (rt != null && rt.IsDefaultReturnType) { + switch (rt.FullyQualifiedName) { + case "System.Byte": + case "System.SByte": + case "System.Int16": + case "System.UInt16": + case "System.Int32": + case "System.UInt32": + case "System.Int64": + case "System.UInt64": + return true; + } + } + return false; + } + + bool IsFloatingPoint(IReturnType rt) + { + if (rt != null && rt.IsDefaultReturnType) { + switch (rt.FullyQualifiedName) { + case "System.Single": + case "System.Double": + case "System.Decimal": + return true; + } + } + return false; + } + + bool NeedsExplicitConversionToString(IReturnType rt) + { + if (rt != null) { + if (rt.IsDefaultReturnType) { + if (rt.FullyQualifiedName == "System.Object" + || !TypeReference.PrimitiveTypesVBReverse.ContainsKey(rt.FullyQualifiedName)) + { + // object and non-primitive types need explicit conversion + return true; + } else { + // primitive types except object don't need explicit conversion + return false; + } + } else { + return true; + } + } + return false; + } + + Expression CreateExplicitConversionToString(Expression expr) + { + return new IdentifierExpression("Convert").Call("ToString", expr); + } + + public override object VisitIdentifierExpression(IdentifierExpression identifierExpression, object data) + { + base.VisitIdentifierExpression(identifierExpression, data); + if (resolver.CompilationUnit == null) + return null; + + InvocationExpression parentIE = identifierExpression.Parent as InvocationExpression; + if (!(identifierExpression.Parent is AddressOfExpression) + && (parentIE == null || parentIE.TargetObject != identifierExpression)) + { + ResolveResult rr = resolver.ResolveInternal(identifierExpression, ExpressionContext.Default); + if (IsMethodGroup(rr)) { + ReplaceCurrentNode(new AddressOfExpression(identifierExpression)); + } + } + return null; + } + + public override object VisitMemberReferenceExpression(MemberReferenceExpression fieldReferenceExpression, object data) + { + base.VisitMemberReferenceExpression(fieldReferenceExpression, data); + + if (resolver.CompilationUnit == null) + return null; + + InvocationExpression parentIE = fieldReferenceExpression.Parent as InvocationExpression; + if (!(fieldReferenceExpression.Parent is AddressOfExpression) + && (parentIE == null || parentIE.TargetObject != fieldReferenceExpression)) + { + ResolveResult rr = resolver.ResolveInternal(fieldReferenceExpression, ExpressionContext.Default); + if (IsMethodGroup(rr)) { + ReplaceCurrentNode(new AddressOfExpression(fieldReferenceExpression)); + } + } + + return null; + } + + static bool IsMethodGroup(ResolveResult rr) + { + MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult; + if (mgrr != null) { + return mgrr.Methods.Any(g=>g.Count > 0); + } + return false; + } + + void HandleAssignmentStatement(AssignmentExpression assignmentExpression) + { + if (resolver.CompilationUnit == null || assignmentExpression == null) + return; + + if (assignmentExpression.Op == AssignmentOperatorType.Add || assignmentExpression.Op == AssignmentOperatorType.Subtract) { + ResolveResult rr = resolver.ResolveInternal(assignmentExpression.Left, ExpressionContext.Default); + if (rr is MemberResolveResult && (rr as MemberResolveResult).ResolvedMember is IEvent) { + if (assignmentExpression.Op == AssignmentOperatorType.Add) { + ReplaceCurrentNode(new AddHandlerStatement(assignmentExpression.Left, assignmentExpression.Right)); + } else { + ReplaceCurrentNode(new RemoveHandlerStatement(assignmentExpression.Left, assignmentExpression.Right)); + } + } else if (rr != null && rr.ResolvedType != null) { + IClass c = rr.ResolvedType.GetUnderlyingClass(); + if (c != null && c.ClassType == ClassType.Delegate) { + InvocationExpression invocation = + new IdentifierExpression("Delegate").Call( + assignmentExpression.Op == AssignmentOperatorType.Add ? "Combine" : "Remove", + assignmentExpression.Left); + invocation.Arguments.Add(assignmentExpression.Right); + + assignmentExpression.Op = AssignmentOperatorType.Assign; + assignmentExpression.Right = new CastExpression( + Refactoring.CodeGenerator.ConvertType(rr.ResolvedType, CreateContext()), + invocation, CastType.Cast); + } + } + } + } + + public override object VisitCastExpression(CastExpression castExpression, object data) + { + base.VisitCastExpression(castExpression, data); + + if (resolver.CompilationUnit == null) + return null; + + IReturnType targetType = ResolveType(castExpression.CastTo); + IClass targetClass = targetType != null ? targetType.GetUnderlyingClass() : null; + if (castExpression.CastType != CastType.TryCast) { + if (targetClass != null && (targetClass.ClassType == ClassType.Struct || targetClass.ClassType == ClassType.Enum)) { + // cast to value type is a conversion + castExpression.CastType = CastType.Conversion; + if (IsInteger(targetType)) { + ResolveResult sourceRR = resolver.ResolveInternal(castExpression.Expression, ExpressionContext.Default); + IReturnType sourceType = sourceRR != null ? sourceRR.ResolvedType : null; + if (IsFloatingPoint(sourceType)) { + // casts from float to int in C# truncate, but VB rounds + // we'll have to introduce a call to Math.Truncate + castExpression.Expression = ExpressionBuilder.Identifier("Math").Call("Truncate", castExpression.Expression); + } else if (sourceType != null && sourceType.FullyQualifiedName == "System.Char") { + // casts from char to int are valid in C#, but need to use AscW in VB + castExpression.Expression = ExpressionBuilder.Identifier("AscW").Call(castExpression.Expression); + if (targetType != null && targetType.FullyQualifiedName == "System.Int32") { + // AscW already returns int, so skip the cast + ReplaceCurrentNode(castExpression.Expression); + return null; + } + } + } + } + if (targetClass != null && targetClass.FullyQualifiedName == "System.Char") { + // C# cast to char is done using ChrW function + ResolveResult sourceRR = resolver.ResolveInternal(castExpression.Expression, ExpressionContext.Default); + IReturnType sourceType = sourceRR != null ? sourceRR.ResolvedType : null; + if (IsInteger(sourceType)) { + ReplaceCurrentNode(new IdentifierExpression("ChrW").Call(castExpression.Expression)); + } + } + } + return null; + } + + public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data) + { + base.VisitUnaryOperatorExpression(unaryOperatorExpression, data); + switch (unaryOperatorExpression.Op) { + case UnaryOperatorType.Dereference: + ReplaceCurrentNode(unaryOperatorExpression.Expression.Member("Target")); + break; + case UnaryOperatorType.AddressOf: + ResolveResult rr = resolver.ResolveInternal(unaryOperatorExpression.Expression, ExpressionContext.Default); + if (rr != null && rr.ResolvedType != null) { + TypeReference targetType = Refactoring.CodeGenerator.ConvertType(rr.ResolvedType, CreateContext()); + TypeReference pointerType = new TypeReference("Pointer", new List { targetType }); + ReplaceCurrentNode(pointerType.New(unaryOperatorExpression.Expression)); + } + break; + } + return null; + } + + public override object VisitTypeReference(TypeReference typeReference, object data) + { + while (typeReference.PointerNestingLevel > 0) { + TypeReference tr = new TypeReference(typeReference.Type) { + IsKeyword = typeReference.IsKeyword, + IsGlobal = typeReference.IsGlobal, + }; + tr.GenericTypes.AddRange(typeReference.GenericTypes); + + typeReference = new TypeReference("Pointer") { + StartLocation = typeReference.StartLocation, + EndLocation = typeReference.EndLocation, + PointerNestingLevel = typeReference.PointerNestingLevel - 1, + GenericTypes = { tr }, + RankSpecifier = typeReference.RankSpecifier + }; + } + ReplaceCurrentNode(typeReference); + return base.VisitTypeReference(typeReference, data); + } + + public override object VisitUnsafeStatement(UnsafeStatement unsafeStatement, object data) + { + base.VisitUnsafeStatement(unsafeStatement, data); + ReplaceCurrentNode(unsafeStatement.Block); + return null; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CodeSnippetConverter.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CodeSnippetConverter.cs new file mode 100644 index 000000000..c79c07f8c --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CodeSnippetConverter.cs @@ -0,0 +1,202 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.PrettyPrinter; + +namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver +{ + /// + /// Allows converting code snippets between C# and VB. + /// This class isn't used by SharpDevelop itself (because it doesn't support projects). + /// It works by creating a dummy project for the file to convert with a set of default references. + /// + public class CodeSnippetConverter + { + /// + /// Project-wide imports to add to all files when converting VB to C#. + /// + public IList DefaultImportsToAdd = new List { "Microsoft.VisualBasic", "System", "System.Collections", "System.Collections.Generic", "System.Data", "System.Diagnostics" }; + + /// + /// Imports to remove (because they will become project-wide imports) when converting C# to VB. + /// + public IList DefaultImportsToRemove = new List { "Microsoft.VisualBasic", "System" }; + + /// + /// References project contents, for resolving type references during the conversion. + /// + public IList ReferencedContents = new List(); + + DefaultProjectContent project; + List specials; + CompilationUnit compilationUnit; + ParseInformation parseInfo; + bool wasExpression; + + #region Parsing + INode Parse(SupportedLanguage sourceLanguage, string sourceCode, out string error) + { + project = new DefaultProjectContent(); + project.ReferencedContents.AddRange(ReferencedContents); + if (sourceLanguage == SupportedLanguage.VBNet) { + project.Language = LanguageProperties.VBNet; + project.DefaultImports = new DefaultUsing(project); + project.DefaultImports.Usings.AddRange(DefaultImportsToAdd); + } else { + project.Language = LanguageProperties.CSharp; + } + SnippetParser parser = new SnippetParser(sourceLanguage); + INode result = parser.Parse(sourceCode); + error = parser.Errors.ErrorOutput; + specials = parser.Specials; + if (parser.Errors.Count != 0) + return null; + + wasExpression = parser.SnippetType == SnippetType.Expression; + if (wasExpression) { + // Special case 'Expression': expressions may be replaced with other statements in the AST by the ConvertVisitor, + // but we need to return a 'stable' node so that the correct transformed AST is returned. + // Thus, we wrap any expressions into a statement block. + result = MakeBlockFromExpression((Expression)result); + } + + // now create a dummy compilation unit around the snippet result + switch (parser.SnippetType) { + case SnippetType.CompilationUnit: + compilationUnit = (CompilationUnit)result; + break; + case SnippetType.Expression: + case SnippetType.Statements: + compilationUnit = MakeCompilationUnitFromTypeMembers( + MakeMethodFromBlock( + (BlockStatement)result + )); + break; + case SnippetType.TypeMembers: + compilationUnit = MakeCompilationUnitFromTypeMembers(result.Children); + break; + default: + throw new NotSupportedException("Unknown snippet type: " + parser.SnippetType); + } + + // convert NRefactory CU in DOM CU + NRefactoryASTConvertVisitor visitor = new NRefactoryASTConvertVisitor(project, sourceLanguage); + visitor.VisitCompilationUnit(compilationUnit, null); + visitor.Cu.FileName = sourceLanguage == SupportedLanguage.CSharp ? "a.cs" : "a.vb"; + + // and register the compilation unit in the DOM + foreach (IClass c in visitor.Cu.Classes) { + project.AddClassToNamespaceList(c); + } + parseInfo = new ParseInformation(visitor.Cu); + + return result; + } + + /// + /// Unpacks the expression from a statement block; if it was wrapped earlier. + /// + INode UnpackExpression(INode node) + { + if (wasExpression) { + BlockStatement block = node as BlockStatement; + if (block != null && block.Children.Count == 1) { + ExpressionStatement es = block.Children[0] as ExpressionStatement; + if (es != null) + return es.Expression; + } + } + return node; + } + + BlockStatement MakeBlockFromExpression(Expression expr) + { + return new BlockStatement { + Children = { + new ExpressionStatement(expr) + }, + StartLocation = expr.StartLocation, + EndLocation = expr.EndLocation + }; + } + + INode[] MakeMethodFromBlock(BlockStatement block) + { + return new INode[] { + new MethodDeclaration { + Name = "DummyMethodForConversion", + Body = block, + StartLocation = block.StartLocation, + EndLocation = block.EndLocation + } + }; + } + + CompilationUnit MakeCompilationUnitFromTypeMembers(IList members) + { + TypeDeclaration type = new TypeDeclaration(Modifiers.None, null) { + Name = "DummyTypeForConversion", + StartLocation = members[0].StartLocation, + EndLocation = GetEndLocation(members[members.Count - 1]) + }; + type.Children.AddRange(members); + return new CompilationUnit { + Children = { + type + } + }; + } + + Location GetEndLocation(INode node) + { + // workaround: MethodDeclaration.EndLocation is the end of the method header, + // but for the end of the dummy class we need the body end + MethodDeclaration method = node as MethodDeclaration; + if (method != null && !method.Body.IsNull) + return method.Body.EndLocation; + else + return node.EndLocation; + } + #endregion + + public string CSharpToVB(string input, out string errors) + { + INode node = Parse(SupportedLanguage.CSharp, input, out errors); + if (node == null) + return null; + // apply conversion logic: + compilationUnit.AcceptVisitor( + new CSharpToVBNetConvertVisitor(project, parseInfo) { + DefaultImportsToRemove = DefaultImportsToRemove, + }, + null); + PreprocessingDirective.CSharpToVB(specials); + return CreateCode(UnpackExpression(node), new VBNetOutputVisitor()); + } + + public string VBToCSharp(string input, out string errors) + { + INode node = Parse(SupportedLanguage.VBNet, input, out errors); + if (node == null) + return null; + // apply conversion logic: + compilationUnit.AcceptVisitor( + new VBNetToCSharpConvertVisitor(project, parseInfo), + null); + PreprocessingDirective.VBToCSharp(specials); + return CreateCode(UnpackExpression(node), new CSharpOutputVisitor()); + } + + string CreateCode(INode node, IOutputAstVisitor outputVisitor) + { + using (SpecialNodesInserter.Install(specials, outputVisitor)) { + node.AcceptVisitor(outputVisitor, null); + } + return outputVisitor.Text; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/InferredReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/InferredReturnType.cs new file mode 100644 index 000000000..d5f7941e4 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/InferredReturnType.cs @@ -0,0 +1,45 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.NRefactory.Ast; + +namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver +{ + /// + /// Used for the type of implicitly typed local variables in C# 3.0 or VB 9. + /// + public sealed class InferredReturnType : ProxyReturnType + { + NRefactoryResolver _resolver; + Expression _expression; + IReturnType _baseType; + + internal InferredReturnType(Expression expression, NRefactoryResolver resolver) + { + if (resolver == null) + throw new ArgumentNullException("resolver"); + + _expression = expression; + _resolver = resolver; + } + + public override IReturnType BaseType { + get { + if (_expression != null) { + // prevent infinite recursion: + Expression expr = _expression; + _expression = null; + + ResolveResult rr = _resolver.ResolveInternal(expr, ExpressionContext.Default); + if (rr != null) { + _baseType = rr.ResolvedType; + } + + _resolver = null; + } + return _baseType; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/LambdaParameterReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/LambdaParameterReturnType.cs new file mode 100644 index 000000000..5b2558d9f --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/LambdaParameterReturnType.cs @@ -0,0 +1,65 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.NRefactory.Ast; + +namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver +{ + /// + /// Description of LambdaParameterReturnType. + /// + public class LambdaParameterReturnType : ProxyReturnType + { + LambdaExpression lambda; + int parameterIndex; + string parameterName; + NRefactoryResolver resolver; + + public LambdaParameterReturnType(LambdaExpression lambda, string name, NRefactoryResolver resolver) + { + if (lambda == null) + throw new ArgumentNullException("lambda"); + if (name == null) + throw new ArgumentNullException("name"); + if (resolver == null) + throw new ArgumentNullException("resolver"); + this.lambda = lambda; + this.parameterName = name; + this.parameterIndex = lambda.Parameters.FindIndex(p => p.ParameterName == name); + this.resolver = resolver; + if (parameterIndex < 0) + throw new ArgumentException("there is no lambda parameter with that name"); + } + + IReturnType cachedType; + + public override IReturnType BaseType { + get { + NRefactoryResolver resolver = this.resolver; + LambdaExpression lambda = this.lambda; + if (resolver == null || lambda == null) + return cachedType; + + this.resolver = null; + this.lambda = null; + MemberLookupHelper.Log("Resolving " + this); + IReturnType rt = resolver.GetExpectedTypeFromContext(lambda); + MemberLookupHelper.Log("Resolving " + this + ", got delegate type " + rt); + IMethod sig = CSharp.TypeInference.GetDelegateOrExpressionTreeSignature(rt, true); + if (sig != null && parameterIndex < sig.Parameters.Count) { + MemberLookupHelper.Log("Resolving " + this + ", got type " + rt); + return cachedType = sig.Parameters[parameterIndex].ReturnType; + } + return null; + } + } + + public override string ToString() + { + return "[LambdaParameterReturnType: " + parameterName + + (resolver != null ? " (not yet resolved)" : " (" + cachedType + ")") + + "]"; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/LambdaReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/LambdaReturnType.cs new file mode 100644 index 000000000..fa991b01f --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/LambdaReturnType.cs @@ -0,0 +1,106 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.Visitors; +using ICSharpCode.NRefactory.Ast; + +namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver +{ + public class LambdaReturnType : AnonymousMethodReturnType + { + NRefactoryResolver resolver; + LambdaExpression lambdaExpression; + List returnExpressions = new List(); + + public override bool CanBeConvertedToExpressionTree { + get { return lambdaExpression != null; } + } + + internal LambdaReturnType(LambdaExpression expression, NRefactoryResolver resolver) + : base(resolver.CompilationUnit) + { + this.resolver = resolver; + this.lambdaExpression = expression; + + base.MethodParameters = new List(); + foreach (ParameterDeclarationExpression param in expression.Parameters) { + base.MethodParameters.Add(NRefactoryASTConvertVisitor.CreateParameter(param, resolver.CallingMember as IMethod, resolver.CallingClass, resolver.CompilationUnit)); + } + if (expression.ExpressionBody.IsNull) + expression.StatementBody.AcceptVisitor(new ReturnStatementFinder(returnExpressions), null); + else + returnExpressions.Add(expression.ExpressionBody); + } + + internal LambdaReturnType(AnonymousMethodExpression expression, NRefactoryResolver resolver) + : base(resolver.CompilationUnit) + { + this.resolver = resolver; + + if (expression.HasParameterList) { + base.MethodParameters = new List(); + foreach (ParameterDeclarationExpression param in expression.Parameters) { + base.MethodParameters.Add(NRefactoryASTConvertVisitor.CreateParameter(param, resolver.CallingMember as IMethod, resolver.CallingClass, resolver.CompilationUnit)); + } + } + expression.Body.AcceptVisitor(new ReturnStatementFinder(returnExpressions), null); + } + + sealed class ReturnStatementFinder : AbstractAstVisitor + { + List returnExpressions; + + public ReturnStatementFinder(List returnExpressions) + { + this.returnExpressions = returnExpressions; + } + + public override object VisitReturnStatement(ReturnStatement returnStatement, object data) + { + returnExpressions.Add(returnStatement.Expression); + return base.VisitReturnStatement(returnStatement, data); + } + + public override object VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, object data) + { + return null; + } + + public override object VisitLambdaExpression(LambdaExpression lambdaExpression, object data) + { + return null; + } + } + + public override IReturnType ResolveReturnType(IReturnType[] parameterTypes) + { + if (lambdaExpression == null) + return ResolveReturnType(); + + try { + MemberLookupHelper.Log("LambdaReturnType: SetImplicitLambdaParameterTypes ", parameterTypes); + resolver.SetImplicitLambdaParameterTypes(lambdaExpression, parameterTypes); + return ResolveReturnType(); + } finally { + resolver.UnsetImplicitLambdaParameterTypes(lambdaExpression); + } + } + + public override IReturnType ResolveReturnType() + { + MemberLookupHelper.Log("LambdaReturnType: ResolveReturnType"); + IReturnType result; + if (returnExpressions.Count == 0) + result = resolver.ProjectContent.SystemTypes.Void; + else + result = returnExpressions.Select(rt => resolver.ResolveInternal(rt, ExpressionContext.Default)) + .Select(rr => rr != null ? rr.ResolvedType : null) + .Aggregate((rt1, rt2) => MemberLookupHelper.GetCommonType(resolver.ProjectContent, rt1, rt2)); + MemberLookupHelper.Log("LambdaReturnType: inferred " + result); + return result; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryASTConvertVisitor.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryASTConvertVisitor.cs new file mode 100644 index 000000000..0c6b8f77c --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryASTConvertVisitor.cs @@ -0,0 +1,856 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +// created on 04.08.2003 at 17:49 +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Linq; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Visitors; +using ICSharpCode.SharpDevelop.Dom.VBNet; +using AST = ICSharpCode.NRefactory.Ast; +using RefParser = ICSharpCode.NRefactory; + +namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver +{ + public class NRefactoryASTConvertVisitor : AbstractAstVisitor + { + DefaultCompilationUnit cu; + DefaultUsingScope currentNamespace; + Stack currentClass = new Stack(); + public string VBRootNamespace { get; set; } + + public ICompilationUnit Cu { + get { + return cu; + } + } + + public NRefactoryASTConvertVisitor(IProjectContent projectContent, SupportedLanguage language) + { + if (language == SupportedLanguage.VBNet) + cu = new VBNetCompilationUnit(projectContent); + else + cu = new DefaultCompilationUnit(projectContent); + } + + DefaultClass GetCurrentClass() + { + return currentClass.Count == 0 ? null : currentClass.Peek(); + } + + ModifierEnum ConvertModifier(AST.Modifiers m) + { + if (this.IsVisualBasic) + return ConvertModifier(m, ModifierEnum.Public); + else if (currentClass.Count > 0 && currentClass.Peek().ClassType == ClassType.Interface) + return ConvertModifier(m, ModifierEnum.Public); + else + return ConvertModifier(m, ModifierEnum.Private); + } + + ModifierEnum ConvertTypeModifier(AST.Modifiers m) + { + if (this.IsVisualBasic) + return ConvertModifier(m, ModifierEnum.Public); + if (currentClass.Count > 0) + return ConvertModifier(m, ModifierEnum.Private); + else + return ConvertModifier(m, ModifierEnum.Internal); + } + + ModifierEnum ConvertModifier(AST.Modifiers m, ModifierEnum defaultVisibility) + { + ModifierEnum r = (ModifierEnum)m; + if ((r & ModifierEnum.VisibilityMask) == ModifierEnum.None) + return r | defaultVisibility; + else + return r; + } + + List specials; + + /// + /// Gets/Sets the list of specials used to read the documentation. + /// The list must be sorted by the start position of the specials! + /// + public List Specials { + get { + return specials; + } + set { + specials = value; + } + } + + string GetDocumentation(int line, IList attributes) + { + foreach (AST.AttributeSection att in attributes) { + if (att.StartLocation.Y > 0 && att.StartLocation.Y < line) + line = att.StartLocation.Y; + } + List lines = new List(); + int length = 0; + while (line > 0) { + line--; + string doku = null; + bool foundPreprocessing = false; + var specialsOnLine = GetSpecialsFromLine(line); + foreach (RefParser.ISpecial special in specialsOnLine) { + RefParser.Comment comment = special as RefParser.Comment; + if (comment != null && comment.CommentType == RefParser.CommentType.Documentation) { + doku = comment.CommentText; + break; + } else if (special is RefParser.PreprocessingDirective) { + foundPreprocessing = true; + } + } + if (doku == null && !foundPreprocessing) + break; + if (doku != null) { + length += 2 + doku.Length; + lines.Add(doku); + } + } + StringBuilder b = new StringBuilder(length); + for (int i = lines.Count - 1; i >= 0; --i) { + b.AppendLine(lines[i]); + } + return b.ToString(); + } + + string GetDocumentationFromLine(int line) + { + foreach (RefParser.ISpecial special in GetSpecialsFromLine(line)) { + RefParser.Comment comment = special as RefParser.Comment; + if (comment != null && comment.CommentType == RefParser.CommentType.Documentation) { + return comment.CommentText; + } + } + return null; + } + + IEnumerable GetSpecialsFromLine(int line) + { + List result = new List(); + if (specials == null) return result; + if (line < 0) return result; + // specials is a sorted list: use interpolation search + int left = 0; + int right = specials.Count - 1; + int m; + + while (left <= right) { + int leftLine = specials[left].StartPosition.Y; + if (line < leftLine) + break; + int rightLine = specials[right].StartPosition.Y; + if (line > rightLine) + break; + if (leftLine == rightLine) { + if (leftLine == line) + m = left; + else + break; + } else { + m = (int)(left + Math.BigMul((line - leftLine), (right - left)) / (rightLine - leftLine)); + } + + int mLine = specials[m].StartPosition.Y; + if (mLine < line) { // found line smaller than line we are looking for + left = m + 1; + } else if (mLine > line) { + right = m - 1; + } else { + // correct line found, + // look for first special in that line + while (--m >= 0 && specials[m].StartPosition.Y == line); + // look at all specials in that line: find doku-comment + while (++m < specials.Count && specials[m].StartPosition.Y == line) { + result.Add(specials[m]); + } + break; + } + } + return result; + } + + public override object VisitCompilationUnit(AST.CompilationUnit compilationUnit, object data) + { + if (compilationUnit == null) { + return null; + } + currentNamespace = new DefaultUsingScope(); + if (!string.IsNullOrEmpty(VBRootNamespace)) { + foreach (string name in VBRootNamespace.Split('.')) { + currentNamespace = new DefaultUsingScope { + Parent = currentNamespace, + NamespaceName = PrependCurrentNamespace(name), + }; + currentNamespace.Parent.ChildScopes.Add(currentNamespace); + } + } + cu.UsingScope = currentNamespace; + compilationUnit.AcceptChildren(this, data); + return cu; + } + + public override object VisitUsingDeclaration(AST.UsingDeclaration usingDeclaration, object data) + { + DefaultUsing us = new DefaultUsing(cu.ProjectContent, GetRegion(usingDeclaration.StartLocation, usingDeclaration.EndLocation)); + foreach (AST.Using u in usingDeclaration.Usings) { + u.AcceptVisitor(this, us); + } + currentNamespace.Usings.Add(us); + return data; + } + + public override object VisitUsing(AST.Using u, object data) + { + Debug.Assert(data is DefaultUsing); + DefaultUsing us = (DefaultUsing)data; + if (u.IsAlias) { + IReturnType rt = CreateReturnType(u.Alias); + if (rt != null) { + us.AddAlias(u.Name, rt); + } + } else { + us.Usings.Add(u.Name); + } + return data; + } + + public override object VisitOptionDeclaration(ICSharpCode.NRefactory.Ast.OptionDeclaration optionDeclaration, object data) + { + if (cu is VBNetCompilationUnit) { + VBNetCompilationUnit provider = cu as VBNetCompilationUnit; + + switch (optionDeclaration.OptionType) { + case ICSharpCode.NRefactory.Ast.OptionType.Explicit: + provider.OptionExplicit = optionDeclaration.OptionValue; + break; + case ICSharpCode.NRefactory.Ast.OptionType.Strict: + provider.OptionStrict = optionDeclaration.OptionValue; + break; + case ICSharpCode.NRefactory.Ast.OptionType.CompareBinary: + provider.OptionCompare = CompareKind.Binary; + break; + case ICSharpCode.NRefactory.Ast.OptionType.CompareText: + provider.OptionCompare = CompareKind.Text; + break; + case ICSharpCode.NRefactory.Ast.OptionType.Infer: + provider.OptionInfer = optionDeclaration.OptionValue; + break; + } + + return null; + } + + return base.VisitOptionDeclaration(optionDeclaration, data); + } + + void ConvertAttributes(AST.AttributedNode from, AbstractEntity to) + { + if (from.Attributes.Count == 0) { + to.Attributes = DefaultAttribute.EmptyAttributeList; + } else { + ICSharpCode.NRefactory.Location location = from.Attributes[0].StartLocation; + ClassFinder context; + if (to is IClass) { + context = new ClassFinder((IClass)to, location.Line, location.Column); + } else { + context = new ClassFinder(to.DeclaringType, location.Line, location.Column); + } + to.Attributes = VisitAttributes(from.Attributes, context); + } + } + + List VisitAttributes(IList attributes, ClassFinder context) + { + // TODO Expressions??? + List result = new List(); + foreach (AST.AttributeSection section in attributes) { + + AttributeTarget target = AttributeTarget.None; + if (section.AttributeTarget != null && section.AttributeTarget != "") { + switch (section.AttributeTarget.ToUpperInvariant()) { + case "ASSEMBLY": + target = AttributeTarget.Assembly; + break; + case "FIELD": + target = AttributeTarget.Field; + break; + case "EVENT": + target = AttributeTarget.Event; + break; + case "METHOD": + target = AttributeTarget.Method; + break; + case "MODULE": + target = AttributeTarget.Module; + break; + case "PARAM": + target = AttributeTarget.Param; + break; + case "PROPERTY": + target = AttributeTarget.Property; + break; + case "RETURN": + target = AttributeTarget.Return; + break; + case "TYPE": + target = AttributeTarget.Type; + break; + default: + target = AttributeTarget.None; + break; + + } + } + + foreach (AST.Attribute attribute in section.Attributes) { + List positionalArguments = new List(); + foreach (AST.Expression positionalArgument in attribute.PositionalArguments) { + positionalArguments.Add(ConvertAttributeArgument(positionalArgument)); + } + Dictionary namedArguments = new Dictionary(); + foreach (AST.NamedArgumentExpression namedArgumentExpression in attribute.NamedArguments) { + namedArguments.Add(namedArgumentExpression.Name, ConvertAttributeArgument(namedArgumentExpression.Expression)); + } + result.Add(new DefaultAttribute(new AttributeReturnType(context, attribute.Name), + target, positionalArguments, namedArguments) + { + CompilationUnit = cu, + Region = GetRegion(attribute.StartLocation, attribute.EndLocation) + }); + } + } + return result; + } + + static object ConvertAttributeArgument(AST.Expression expression) + { + AST.PrimitiveExpression pe = expression as AST.PrimitiveExpression; + if (pe != null) + return pe.Value; + else + return null; + } + + public override object VisitAttributeSection(ICSharpCode.NRefactory.Ast.AttributeSection attributeSection, object data) + { + if (GetCurrentClass() == null) { + ClassFinder cf = new ClassFinder(new DefaultClass(cu, "DummyClass"), attributeSection.StartLocation.Line, attributeSection.StartLocation.Column); + cu.Attributes.AddRange(VisitAttributes(new[] { attributeSection }, cf)); + } + return null; + } + + string PrependCurrentNamespace(string name) + { + if (string.IsNullOrEmpty(currentNamespace.NamespaceName)) + return name; + else + return currentNamespace.NamespaceName + "." + name; + } + + public override object VisitNamespaceDeclaration(AST.NamespaceDeclaration namespaceDeclaration, object data) + { + DefaultUsingScope oldNamespace = currentNamespace; + foreach (string name in namespaceDeclaration.Name.Split('.')) { + currentNamespace = new DefaultUsingScope { + Parent = currentNamespace, + NamespaceName = PrependCurrentNamespace(name), + }; + currentNamespace.Parent.ChildScopes.Add(currentNamespace); + } + object ret = namespaceDeclaration.AcceptChildren(this, data); + currentNamespace = oldNamespace; + return ret; + } + + ClassType TranslateClassType(AST.ClassType type) + { + switch (type) { + case AST.ClassType.Enum: + return ClassType.Enum; + case AST.ClassType.Interface: + return ClassType.Interface; + case AST.ClassType.Struct: + return ClassType.Struct; + case AST.ClassType.Module: + return ClassType.Module; + default: + return ClassType.Class; + } + } + + static DomRegion GetRegion(RefParser.Location start, RefParser.Location end) + { + return DomRegion.FromLocation(start, end); + } + + public override object VisitTypeDeclaration(AST.TypeDeclaration typeDeclaration, object data) + { + DomRegion region = GetRegion(typeDeclaration.StartLocation, typeDeclaration.EndLocation); + DomRegion bodyRegion = GetRegion(typeDeclaration.BodyStartLocation, typeDeclaration.EndLocation); + + DefaultClass c = new DefaultClass(cu, TranslateClassType(typeDeclaration.Type), ConvertTypeModifier(typeDeclaration.Modifier), region, GetCurrentClass()); + if (c.IsStatic) { + // static classes are also abstract and sealed at the same time + c.Modifiers |= ModifierEnum.Abstract | ModifierEnum.Sealed; + } + c.BodyRegion = bodyRegion; + ConvertAttributes(typeDeclaration, c); + c.Documentation = GetDocumentation(region.BeginLine, typeDeclaration.Attributes); + + DefaultClass outerClass = GetCurrentClass(); + if (outerClass != null) { + outerClass.InnerClasses.Add(c); + c.FullyQualifiedName = outerClass.FullyQualifiedName + '.' + typeDeclaration.Name; + } else { + c.FullyQualifiedName = PrependCurrentNamespace(typeDeclaration.Name); + cu.Classes.Add(c); + } + c.UsingScope = currentNamespace; + currentClass.Push(c); + + ConvertTemplates(outerClass, typeDeclaration.Templates, c); // resolve constrains in context of the class + // templates must be converted before base types because base types may refer to generic types + + if (c.ClassType != ClassType.Enum && typeDeclaration.BaseTypes != null) { + foreach (AST.TypeReference type in typeDeclaration.BaseTypes) { + IReturnType rt = CreateReturnType(type, null, TypeVisitor.ReturnTypeOptions.BaseTypeReference); + if (rt != null) { + c.BaseTypes.Add(rt); + } + } + } + + object ret = typeDeclaration.AcceptChildren(this, data); + currentClass.Pop(); + + if (c.ClassType == ClassType.Module) { + foreach (DefaultField f in c.Fields) { + f.Modifiers |= ModifierEnum.Static; + } + foreach (DefaultMethod m in c.Methods) { + m.Modifiers |= ModifierEnum.Static; + } + foreach (DefaultProperty p in c.Properties) { + p.Modifiers |= ModifierEnum.Static; + } + foreach (DefaultEvent e in c.Events) { + e.Modifiers |= ModifierEnum.Static; + } + } + + return ret; + } + + void ConvertTemplates(DefaultClass outerClass, IList templateList, DefaultClass c) + { + int outerClassTypeParameterCount = outerClass != null ? outerClass.TypeParameters.Count : 0; + if (templateList.Count == 0 && outerClassTypeParameterCount == 0) { + c.TypeParameters = DefaultTypeParameter.EmptyTypeParameterList; + } else { + Debug.Assert(c.TypeParameters.Count == 0); + + int index = 0; + if (outerClassTypeParameterCount > 0) { + foreach (DefaultTypeParameter outerTypeParamter in outerClass.TypeParameters) { + DefaultTypeParameter p = new DefaultTypeParameter(c, outerTypeParamter.Name, index++); + p.HasConstructableConstraint = outerTypeParamter.HasConstructableConstraint; + p.HasReferenceTypeConstraint = outerTypeParamter.HasReferenceTypeConstraint; + p.HasValueTypeConstraint = outerTypeParamter.HasValueTypeConstraint; + p.Attributes.AddRange(outerTypeParamter.Attributes); + p.Constraints.AddRange(outerTypeParamter.Constraints); + c.TypeParameters.Add(p); + } + } + + foreach (AST.TemplateDefinition template in templateList) { + c.TypeParameters.Add(new DefaultTypeParameter(c, template.Name, index++)); + } + // converting the constraints requires that the type parameters are already present + for (int i = 0; i < templateList.Count; i++) { + ConvertConstraints(templateList[i], (DefaultTypeParameter)c.TypeParameters[i + outerClassTypeParameterCount]); + } + } + } + + void ConvertTemplates(List templateList, DefaultMethod m) + { + int index = 0; + if (templateList.Count == 0) { + m.TypeParameters = DefaultTypeParameter.EmptyTypeParameterList; + } else { + Debug.Assert(m.TypeParameters.Count == 0); + foreach (AST.TemplateDefinition template in templateList) { + m.TypeParameters.Add(new DefaultTypeParameter(m, template.Name, index++)); + } + // converting the constraints requires that the type parameters are already present + for (int i = 0; i < templateList.Count; i++) { + ConvertConstraints(templateList[i], (DefaultTypeParameter)m.TypeParameters[i]); + } + } + } + + void ConvertConstraints(AST.TemplateDefinition template, DefaultTypeParameter typeParameter) + { + foreach (AST.TypeReference typeRef in template.Bases) { + if (typeRef == AST.TypeReference.NewConstraint) { + typeParameter.HasConstructableConstraint = true; + } else if (typeRef == AST.TypeReference.ClassConstraint) { + typeParameter.HasReferenceTypeConstraint = true; + } else if (typeRef == AST.TypeReference.StructConstraint) { + typeParameter.HasValueTypeConstraint = true; + } else { + IReturnType rt = CreateReturnType(typeRef, typeParameter.Method, TypeVisitor.ReturnTypeOptions.None); + if (rt != null) { + typeParameter.Constraints.Add(rt); + } + } + } + } + + public override object VisitDelegateDeclaration(AST.DelegateDeclaration delegateDeclaration, object data) + { + DomRegion region = GetRegion(delegateDeclaration.StartLocation, delegateDeclaration.EndLocation); + DefaultClass c = new DefaultClass(cu, ClassType.Delegate, ConvertTypeModifier(delegateDeclaration.Modifier), region, GetCurrentClass()); + c.Documentation = GetDocumentation(region.BeginLine, delegateDeclaration.Attributes); + ConvertAttributes(delegateDeclaration, c); + CreateDelegate(c, delegateDeclaration.Name, delegateDeclaration.ReturnType, + delegateDeclaration.Templates, delegateDeclaration.Parameters); + return c; + } + + void CreateDelegate(DefaultClass c, string name, AST.TypeReference returnType, IList templates, IList parameters) + { + c.BaseTypes.Add(c.ProjectContent.SystemTypes.MulticastDelegate); + DefaultClass outerClass = GetCurrentClass(); + if (outerClass != null) { + outerClass.InnerClasses.Add(c); + c.FullyQualifiedName = outerClass.FullyQualifiedName + '.' + name; + } else { + c.FullyQualifiedName = PrependCurrentNamespace(name); + cu.Classes.Add(c); + } + c.UsingScope = currentNamespace; + currentClass.Push(c); // necessary for CreateReturnType + ConvertTemplates(outerClass, templates, c); + + List p = new List(); + if (parameters != null) { + foreach (AST.ParameterDeclarationExpression param in parameters) { + p.Add(CreateParameter(param)); + } + } + AnonymousMethodReturnType.AddDefaultDelegateMethod(c, CreateReturnType(returnType), p); + + currentClass.Pop(); + } + + IParameter CreateParameter(AST.ParameterDeclarationExpression par) + { + return CreateParameter(par, null); + } + + IParameter CreateParameter(AST.ParameterDeclarationExpression par, IMethod method) + { + return CreateParameter(par, method, GetCurrentClass(), cu); + } + + internal static IParameter CreateParameter(AST.ParameterDeclarationExpression par, IMethod method, IClass currentClass, ICompilationUnit cu) + { + IReturnType parType = CreateReturnType(par.TypeReference, method, currentClass, cu, TypeVisitor.ReturnTypeOptions.None); + DefaultParameter p = new DefaultParameter(par.ParameterName, parType, GetRegion(par.StartLocation, par.EndLocation)); + p.Modifiers = (ParameterModifiers)par.ParamModifier; + return p; + } + + public override object VisitMethodDeclaration(AST.MethodDeclaration methodDeclaration, object data) + { + DomRegion region = GetRegion(methodDeclaration.StartLocation, methodDeclaration.EndLocation); + DomRegion bodyRegion = GetRegion(methodDeclaration.EndLocation, methodDeclaration.Body != null ? methodDeclaration.Body.EndLocation : RefParser.Location.Empty); + DefaultClass currentClass = GetCurrentClass(); + + DefaultMethod method = new DefaultMethod(methodDeclaration.Name, null, ConvertModifier(methodDeclaration.Modifier), region, bodyRegion, currentClass); + method.Documentation = GetDocumentation(region.BeginLine, methodDeclaration.Attributes); + ConvertTemplates(methodDeclaration.Templates, method); + method.ReturnType = CreateReturnType(methodDeclaration.TypeReference, method, TypeVisitor.ReturnTypeOptions.None); + ConvertAttributes(methodDeclaration, method); + method.IsExtensionMethod = methodDeclaration.IsExtensionMethod + || method.Attributes.Any(att => att.AttributeType != null && att.AttributeType.FullyQualifiedName == "System.Runtime.CompilerServices.ExtensionAttribute"); + if (methodDeclaration.Parameters.Count > 0) { + foreach (AST.ParameterDeclarationExpression par in methodDeclaration.Parameters) { + method.Parameters.Add(CreateParameter(par, method)); + } + } else { + method.Parameters = DefaultParameter.EmptyParameterList; + } + if (methodDeclaration.HandlesClause.Count > 0) { + foreach (string handlesClause in methodDeclaration.HandlesClause) { + if (handlesClause.ToLowerInvariant().StartsWith("me.")) + method.HandlesClauses.Add(handlesClause.Substring(3)); + else if (handlesClause.ToLowerInvariant().StartsWith("mybase.")) + method.HandlesClauses.Add(handlesClause.Substring(7)); + else + method.HandlesClauses.Add(handlesClause); + } + } else { + method.HandlesClauses = EmptyList.Instance; + } + + AddInterfaceImplementations(method, methodDeclaration); + + currentClass.Methods.Add(method); + return null; + } + + public override object VisitDeclareDeclaration(AST.DeclareDeclaration declareDeclaration, object data) + { + DefaultClass currentClass = GetCurrentClass(); + + DomRegion region = GetRegion(declareDeclaration.StartLocation, declareDeclaration.EndLocation); + DefaultMethod method = new DefaultMethod(declareDeclaration.Name, null, ConvertModifier(declareDeclaration.Modifier), region, DomRegion.Empty, currentClass); + method.Documentation = GetDocumentation(region.BeginLine, declareDeclaration.Attributes); + method.Modifiers |= ModifierEnum.Extern | ModifierEnum.Static; + + method.ReturnType = CreateReturnType(declareDeclaration.TypeReference, method, TypeVisitor.ReturnTypeOptions.None); + ConvertAttributes(declareDeclaration, method); + + foreach (AST.ParameterDeclarationExpression par in declareDeclaration.Parameters) { + method.Parameters.Add(CreateParameter(par, method)); + } + + currentClass.Methods.Add(method); + return null; + } + + public override object VisitOperatorDeclaration(AST.OperatorDeclaration operatorDeclaration, object data) + { + DefaultClass c = GetCurrentClass(); + DomRegion region = GetRegion(operatorDeclaration.StartLocation, operatorDeclaration.EndLocation); + DomRegion bodyRegion = GetRegion(operatorDeclaration.EndLocation, operatorDeclaration.Body != null ? operatorDeclaration.Body.EndLocation : RefParser.Location.Empty); + + DefaultMethod method = new DefaultMethod(operatorDeclaration.Name, CreateReturnType(operatorDeclaration.TypeReference), ConvertModifier(operatorDeclaration.Modifier), region, bodyRegion, c); + method.Documentation = GetDocumentation(region.BeginLine, operatorDeclaration.Attributes); + ConvertAttributes(operatorDeclaration, method); + if(operatorDeclaration.Parameters != null) + { + foreach (AST.ParameterDeclarationExpression par in operatorDeclaration.Parameters) { + method.Parameters.Add(CreateParameter(par, method)); + } + } + AddInterfaceImplementations(method, operatorDeclaration); + c.Methods.Add(method); + return null; + } + + public override object VisitConstructorDeclaration(AST.ConstructorDeclaration constructorDeclaration, object data) + { + DomRegion region = GetRegion(constructorDeclaration.StartLocation, constructorDeclaration.EndLocation); + DomRegion bodyRegion = GetRegion(constructorDeclaration.EndLocation, constructorDeclaration.Body != null ? constructorDeclaration.Body.EndLocation : RefParser.Location.Empty); + DefaultClass c = GetCurrentClass(); + + Constructor constructor = new Constructor(ConvertModifier(constructorDeclaration.Modifier), region, bodyRegion, GetCurrentClass()); + constructor.Documentation = GetDocumentation(region.BeginLine, constructorDeclaration.Attributes); + ConvertAttributes(constructorDeclaration, constructor); + if (constructorDeclaration.Parameters != null) { + foreach (AST.ParameterDeclarationExpression par in constructorDeclaration.Parameters) { + constructor.Parameters.Add(CreateParameter(par)); + } + } + + if (constructor.Modifiers.HasFlag(ModifierEnum.Static)) + constructor.Modifiers = ConvertModifier(constructorDeclaration.Modifier, ModifierEnum.None); + + c.Methods.Add(constructor); + return null; + } + + public override object VisitDestructorDeclaration(AST.DestructorDeclaration destructorDeclaration, object data) + { + DomRegion region = GetRegion(destructorDeclaration.StartLocation, destructorDeclaration.EndLocation); + DomRegion bodyRegion = GetRegion(destructorDeclaration.EndLocation, destructorDeclaration.Body != null ? destructorDeclaration.Body.EndLocation : RefParser.Location.Empty); + + DefaultClass c = GetCurrentClass(); + + Destructor destructor = new Destructor(region, bodyRegion, c); + ConvertAttributes(destructorDeclaration, destructor); + c.Methods.Add(destructor); + return null; + } + + bool IsVisualBasic { + get { + return cu.ProjectContent.Language == LanguageProperties.VBNet; + } + } + + public override object VisitFieldDeclaration(AST.FieldDeclaration fieldDeclaration, object data) + { + DomRegion region = GetRegion(fieldDeclaration.StartLocation, fieldDeclaration.EndLocation); + DefaultClass c = GetCurrentClass(); + ModifierEnum modifier = ConvertModifier(fieldDeclaration.Modifier, + (c.ClassType == ClassType.Struct && this.IsVisualBasic) + ? ModifierEnum.Public : ModifierEnum.Private); + string doku = GetDocumentation(region.BeginLine, fieldDeclaration.Attributes); + if (currentClass.Count > 0) { + for (int i = 0; i < fieldDeclaration.Fields.Count; ++i) { + AST.VariableDeclaration field = (AST.VariableDeclaration)fieldDeclaration.Fields[i]; + + IReturnType retType; + if (c.ClassType == ClassType.Enum) { + retType = c.DefaultReturnType; + } else { + retType = CreateReturnType(fieldDeclaration.GetTypeForField(i)); + if (!field.FixedArrayInitialization.IsNull) + retType = new ArrayReturnType(cu.ProjectContent, retType, 1); + } + DefaultField f = new DefaultField(retType, field.Name, modifier, region, c); + ConvertAttributes(fieldDeclaration, f); + f.Documentation = doku; + if (c.ClassType == ClassType.Enum) { + f.Modifiers = ModifierEnum.Const | ModifierEnum.Public; + } + + c.Fields.Add(f); + } + } + return null; + } + + public override object VisitPropertyDeclaration(AST.PropertyDeclaration propertyDeclaration, object data) + { + DomRegion region = GetRegion(propertyDeclaration.StartLocation, propertyDeclaration.EndLocation); + DomRegion bodyRegion = GetRegion(propertyDeclaration.BodyStart, propertyDeclaration.BodyEnd); + + IReturnType type = CreateReturnType(propertyDeclaration.TypeReference); + DefaultClass c = GetCurrentClass(); + + DefaultProperty property = new DefaultProperty(propertyDeclaration.Name, type, ConvertModifier(propertyDeclaration.Modifier), region, bodyRegion, GetCurrentClass()); + if (propertyDeclaration.HasGetRegion) { + property.GetterRegion = GetRegion(propertyDeclaration.GetRegion.StartLocation, propertyDeclaration.GetRegion.EndLocation); + property.CanGet = true; + property.GetterModifiers = ConvertModifier(propertyDeclaration.GetRegion.Modifier, ModifierEnum.None); + } + if (propertyDeclaration.HasSetRegion) { + property.SetterRegion = GetRegion(propertyDeclaration.SetRegion.StartLocation, propertyDeclaration.SetRegion.EndLocation); + property.CanSet = true; + property.SetterModifiers = ConvertModifier(propertyDeclaration.SetRegion.Modifier, ModifierEnum.None); + } + property.Documentation = GetDocumentation(region.BeginLine, propertyDeclaration.Attributes); + ConvertAttributes(propertyDeclaration, property); + + property.IsIndexer = propertyDeclaration.IsIndexer; + + if (propertyDeclaration.Parameters != null) { + foreach (AST.ParameterDeclarationExpression par in propertyDeclaration.Parameters) { + property.Parameters.Add(CreateParameter(par)); + } + } + // If an IndexerNameAttribute is specified, use the specified name + // for the indexer instead of the default name. + IAttribute indexerNameAttribute = property.Attributes.LastOrDefault(this.IsIndexerNameAttribute); + if (indexerNameAttribute != null && indexerNameAttribute.PositionalArguments.Count > 0) { + string name = indexerNameAttribute.PositionalArguments[0] as string; + if (!String.IsNullOrEmpty(name)) { + property.FullyQualifiedName = String.Concat(property.DeclaringType.FullyQualifiedName, ".", name); + } + } + + AddInterfaceImplementations(property, propertyDeclaration); + c.Properties.Add(property); + return null; + } + + bool IsIndexerNameAttribute(IAttribute att) + { + if (att == null || att.AttributeType == null) + return false; + string indexerNameAttributeFullName = typeof(System.Runtime.CompilerServices.IndexerNameAttribute).FullName; + IClass indexerNameAttributeClass = this.Cu.ProjectContent.GetClass(indexerNameAttributeFullName, 0, LanguageProperties.CSharp, GetClassOptions.Default | GetClassOptions.ExactMatch); + if (indexerNameAttributeClass == null) { + return String.Equals(att.AttributeType.FullyQualifiedName, indexerNameAttributeFullName, StringComparison.Ordinal); + } + return att.AttributeType.Equals(indexerNameAttributeClass.DefaultReturnType); + } + + public override object VisitEventDeclaration(AST.EventDeclaration eventDeclaration, object data) + { + DomRegion region = GetRegion(eventDeclaration.StartLocation, eventDeclaration.EndLocation); + DomRegion bodyRegion = GetRegion(eventDeclaration.BodyStart, eventDeclaration.BodyEnd); + DefaultClass c = GetCurrentClass(); + + IReturnType type; + if (eventDeclaration.TypeReference.IsNull) { + DefaultClass del = new DefaultClass(cu, ClassType.Delegate, + ConvertModifier(eventDeclaration.Modifier), + region, c); + del.Modifiers |= ModifierEnum.Synthetic; + CreateDelegate(del, eventDeclaration.Name + "EventHandler", + new AST.TypeReference("System.Void", true), + new AST.TemplateDefinition[0], + eventDeclaration.Parameters); + type = del.DefaultReturnType; + } else { + type = CreateReturnType(eventDeclaration.TypeReference); + } + DefaultEvent e = new DefaultEvent(eventDeclaration.Name, type, ConvertModifier(eventDeclaration.Modifier), region, bodyRegion, c); + ConvertAttributes(eventDeclaration, e); + AddInterfaceImplementations(e, eventDeclaration); + c.Events.Add(e); + + e.Documentation = GetDocumentation(region.BeginLine, eventDeclaration.Attributes); + if (eventDeclaration.HasAddRegion) { + e.AddMethod = new DefaultMethod(e.DeclaringType, "add_" + e.Name) { + Parameters = { new DefaultParameter("value", e.ReturnType, DomRegion.Empty) }, + Region = GetRegion(eventDeclaration.AddRegion.StartLocation, eventDeclaration.AddRegion.EndLocation), + BodyRegion = GetRegion(eventDeclaration.AddRegion.Block.StartLocation, eventDeclaration.AddRegion.Block.EndLocation) + }; + } + if (eventDeclaration.HasRemoveRegion) { + e.RemoveMethod = new DefaultMethod(e.DeclaringType, "remove_" + e.Name) { + Parameters = { new DefaultParameter("value", e.ReturnType, DomRegion.Empty) }, + Region = GetRegion(eventDeclaration.RemoveRegion.StartLocation, eventDeclaration.RemoveRegion.EndLocation), + BodyRegion = GetRegion(eventDeclaration.RemoveRegion.Block.StartLocation, eventDeclaration.RemoveRegion.Block.EndLocation) + }; + } + return null; + } + + void AddInterfaceImplementations(AbstractMember member, AST.MemberNode memberNode) + { + member.InterfaceImplementations.AddRange( + memberNode.InterfaceImplementations + .Select(x => new ExplicitInterfaceImplementation(CreateReturnType(x.InterfaceType), x.MemberName)) + ); + if (!IsVisualBasic && member.InterfaceImplementations.Any()) { + member.Modifiers = ConvertModifier(memberNode.Modifier, ModifierEnum.None); + } + } + + IReturnType CreateReturnType(AST.TypeReference reference, IMethod method, TypeVisitor.ReturnTypeOptions options) + { + return CreateReturnType(reference, method, GetCurrentClass(), cu, options); + } + + static IReturnType CreateReturnType(AST.TypeReference reference, IMethod method, IClass currentClass, ICompilationUnit cu, TypeVisitor.ReturnTypeOptions options) + { + if (currentClass == null) { + return TypeVisitor.CreateReturnType(reference, new DefaultClass(cu, "___DummyClass"), method, 1, 1, cu.ProjectContent, options | TypeVisitor.ReturnTypeOptions.Lazy); + } else { + return TypeVisitor.CreateReturnType(reference, currentClass, method, currentClass.Region.BeginLine + 1, 1, cu.ProjectContent, options | TypeVisitor.ReturnTypeOptions.Lazy); + } + } + + IReturnType CreateReturnType(AST.TypeReference reference) + { + return CreateReturnType(reference, null, TypeVisitor.ReturnTypeOptions.None); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryInformationProvider.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryInformationProvider.cs new file mode 100644 index 000000000..c400a8def --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryInformationProvider.cs @@ -0,0 +1,38 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Ast; + +namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver +{ + public class NRefactoryInformationProvider : IEnvironmentInformationProvider + { + IProjectContent _projectContent; + + public NRefactoryInformationProvider(IProjectContent projectContent) + { + if (projectContent == null) + throw new ArgumentNullException("projectContent"); + _projectContent = projectContent; + } + + public bool HasField(string reflectionTypeName, int typeParameterCount, string fieldName) + { + IClass c; + if (typeParameterCount > 0) { + c = _projectContent.GetClass(reflectionTypeName, typeParameterCount); + } else { + c = _projectContent.GetClassByReflectionName(reflectionTypeName, true); + } + if (c == null) + return false; + foreach (IField field in c.DefaultReturnType.GetFields()) { + if (field.Name == fieldName) + return true; + } + return false; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs new file mode 100644 index 000000000..256c3c278 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs @@ -0,0 +1,1469 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; + +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.Visitors; +using ICSharpCode.SharpDevelop.Dom.CSharp; +using ICSharpCode.SharpDevelop.Dom.VBNet; +using NR = ICSharpCode.NRefactory; + +namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver +{ + /// + /// NRefactoryResolver implements the IResolver interface for the NRefactory languages (C# and VB). + /// + /// + /// About implementing code-completion for other languages: + /// + /// It possible to convert from your AST to NRefactory (to C# or VB) (or even let your parser create + /// NRefactory AST objects directly), but then code-completion might be incorrect when the rules of your language + /// differ from the C#/VB language rules. + /// If you want to correctly implement code-completion for your own language, you should implement your own resolver. + /// + public class NRefactoryResolver : IResolver + { + ICompilationUnit cu; + IClass callingClass; + IMember callingMember; + ICSharpCode.NRefactory.Visitors.LookupTableVisitor lookupTableVisitor; + IProjectContent projectContent; + + readonly NR.SupportedLanguage language; + + int caretLine; + int caretColumn; + + bool inferAllowed; + internal bool allowMethodGroupResolveResult; + + public NR.SupportedLanguage Language { + get { + return language; + } + } + + public IProjectContent ProjectContent { + get { + return projectContent; + } + set { + if (value == null) + throw new ArgumentNullException("value"); + projectContent = value; + } + } + + public ICompilationUnit CompilationUnit { + get { + return cu; + } + } + + public IClass CallingClass { + get { + return callingClass; + } + } + + public IMember CallingMember { + get { + return callingMember; + } + } + + public int CaretLine { + get { + return caretLine; + } + } + + public int CaretColumn { + get { + return caretColumn; + } + } + + readonly LanguageProperties languageProperties; + + public LanguageProperties LanguageProperties { + get { + return languageProperties; + } + } + + public NRefactoryResolver(LanguageProperties languageProperties) + { + if (languageProperties == null) + throw new ArgumentNullException("languageProperties"); + this.languageProperties = languageProperties; + if (languageProperties is LanguageProperties.CSharpProperties) { + language = NR.SupportedLanguage.CSharp; + inferAllowed = true; + allowMethodGroupResolveResult = true; + } else if (languageProperties is LanguageProperties.VBNetProperties) { + language = NR.SupportedLanguage.VBNet; + inferAllowed = false; + allowMethodGroupResolveResult = false; + } else { + throw new NotSupportedException("The language " + languageProperties.ToString() + " is not supported in the NRefactoryResolver"); + } + } + + Expression ParseExpression(string expression, int caretColumnOffset) + { + Expression expr = SpecialConstructs(expression); + if (expr == null) { + // SEMICOLON HACK: Parsing expressions without trailing semicolon does not work correctly + if (language == NR.SupportedLanguage.CSharp && !expression.EndsWith(";")) + expression += ";"; + using (NR.IParser p = NR.ParserFactory.CreateParser(language, new System.IO.StringReader(expression))) { + p.Lexer.SetInitialLocation(new NR.Location(caretColumn + caretColumnOffset, caretLine)); + expr = p.ParseExpression(); + } + } + return expr; + } + + + string GetFixedExpression(ExpressionResult expressionResult) + { + string expression = expressionResult.Expression; + if (expression == null) { + expression = ""; + } + expression = expression.TrimStart(); + + return expression; + } + + public bool Initialize(ParseInformation parseInfo, int caretLine, int caretColumn) + { + this.caretLine = caretLine; + this.caretColumn = caretColumn; + callingClass = null; + callingMember = null; + + if (parseInfo == null) { + return false; + } + + cu = parseInfo.CompilationUnit; + if (cu == null || cu.ProjectContent == null) { + return false; + } + this.ProjectContent = cu.ProjectContent; + + if (language == SupportedLanguage.VBNet) { + IVBNetOptionProvider provider = (IVBNetOptionProvider)cu; + + inferAllowed = provider.OptionInfer ?? false; + } + + callingClass = cu.GetInnermostClass(caretLine, caretColumn); + callingMember = GetCallingMember(); + return true; + } + + public ResolveResult Resolve(ExpressionResult expressionResult, + ParseInformation parseInfo, + string fileContent) + { + string expression = GetFixedExpression(expressionResult); + + if (!Initialize(parseInfo, expressionResult.Region.BeginLine, expressionResult.Region.BeginColumn)) + return null; + + Expression expr = null; + if (language == NR.SupportedLanguage.VBNet) { + if (expression.Length == 0 || expression[0] == '.' && (expression.Length > 1 && !char.IsDigit(expression[1]))) { + return WithResolve(expression, fileContent); + } else if ("global".Equals(expression, StringComparison.InvariantCultureIgnoreCase)) { + return new NamespaceResolveResult(null, null, ""); + } + // array + } + if (expressionResult.Context.IsTypeContext && !expressionResult.Context.IsObjectCreation) { + expr = ParseTypeReference(expression, language); + } + if (expr == null) { + expr = ParseExpression(expression, 0); + if (expr == null) { + return null; + } + + // "new" is missing + if (expressionResult.Context.IsObjectCreation && !(expr is ObjectCreateExpression)) { + Expression tmp = expr; + while (tmp != null) { + if (tmp is IdentifierExpression) + return ResolveInternal(expr, ExpressionContext.Type); + if (tmp is MemberReferenceExpression) + tmp = (tmp as MemberReferenceExpression).TargetObject; + else + break; + } + expr = ParseExpression("new " + expression, -4); + if (expr == null) { + return null; + } + } + } + + ResolveResult rr; + if (expressionResult.Context == ExpressionContext.Attribute) { + return ResolveAttribute(expr, new NR.Location(caretColumn, caretLine)); + } else if (expressionResult.Context == CSharpExpressionContext.ObjectInitializer && expr is IdentifierExpression) { + bool isCollectionInitializer; + rr = ResolveObjectInitializer((expr as IdentifierExpression).Identifier, fileContent, out isCollectionInitializer); + if (!isCollectionInitializer || rr != null) { + return rr; + } + } + + RunLookupTableVisitor(fileContent); + + rr = CtrlSpaceResolveHelper.GetResultFromDeclarationLine(callingClass, callingMember as IMethodOrProperty, caretLine, caretColumn, expressionResult); + if (rr != null) return rr; + + return ResolveInternal(expr, expressionResult.Context); + } + + TypeReferenceExpression ParseTypeReference(string typeReference, SupportedLanguage language) + { + string typeOfFunc = "typeof"; + + if (language == SupportedLanguage.VBNet) + typeOfFunc = "GetType"; + + TypeOfExpression toe = ParseExpression(typeOfFunc + "(" + typeReference + ")", -(typeOfFunc.Length + 1)) as TypeOfExpression; + + if (toe != null) + return new TypeReferenceExpression(toe.TypeReference); + + return null; + } + + ResolveResult WithResolve(string expression, string fileContent) + { + RunLookupTableVisitor(fileContent); + + WithStatement innermost = null; + if (lookupTableVisitor.WithStatements != null) { + foreach (WithStatement with in lookupTableVisitor.WithStatements) { + if (IsInside(new NR.Location(caretColumn, caretLine), with.StartLocation, with.EndLocation)) { + innermost = with; + } + } + } + if (innermost != null) { + if (expression.Length > 1) { + Expression expr = ParseExpression(DummyFindVisitor.dummyName + expression, -DummyFindVisitor.dummyName.Length); + if (expr == null) return null; + DummyFindVisitor v = new DummyFindVisitor(); + expr.AcceptVisitor(v, null); + if (v.result == null) return null; + v.result.TargetObject = innermost.Expression; + return ResolveInternal(expr, ExpressionContext.Default); + } else { + return ResolveInternal(innermost.Expression, ExpressionContext.Default); + } + } else { + return null; + } + } + private class DummyFindVisitor : AbstractAstVisitor { + internal const string dummyName = "___withStatementExpressionDummy"; + internal MemberReferenceExpression result; + public override object VisitMemberReferenceExpression(MemberReferenceExpression fieldReferenceExpression, object data) + { + IdentifierExpression ie = fieldReferenceExpression.TargetObject as IdentifierExpression; + if (ie != null && ie.Identifier == dummyName) + result = fieldReferenceExpression; + return base.VisitMemberReferenceExpression(fieldReferenceExpression, data); + } + } + + public INode ParseCurrentMember(string fileContent) + { + CompilationUnit cu = ParseCurrentMemberAsCompilationUnit(fileContent); + if (cu != null && cu.Children.Count > 0) { + TypeDeclaration td = cu.Children[0] as TypeDeclaration; + if (td != null && td.Children.Count > 0) { + return td.Children[0]; + } + } + return null; + } + + public CompilationUnit ParseCurrentMemberAsCompilationUnit(string fileContent) + { + System.IO.TextReader content = ExtractCurrentMethod(fileContent); + if (content != null) { + NR.IParser p = NR.ParserFactory.CreateParser(language, content); + p.Parse(); + return p.CompilationUnit; + } else { + return null; + } + } + + void RunLookupTableVisitor(string fileContent) + { + lookupTableVisitor = new LookupTableVisitor(language); + + if (callingMember != null) { + CompilationUnit cu = ParseCurrentMemberAsCompilationUnit(fileContent); + if (cu != null) { + lookupTableVisitor.VisitCompilationUnit(cu, null); + } + } + } + + public void RunLookupTableVisitor(INode currentMemberNode) + { + if (currentMemberNode == null) + throw new ArgumentNullException("currentMemberNode"); + lookupTableVisitor = new LookupTableVisitor(language); + currentMemberNode.AcceptVisitor(lookupTableVisitor, null); + } + + string GetAttributeName(Expression expr) + { + if (expr is IdentifierExpression) { + return (expr as IdentifierExpression).Identifier; + } else if (expr is MemberReferenceExpression) { + ResolveVisitor typeVisitor = new ResolveVisitor(this); + MemberReferenceExpression fieldReferenceExpression = (MemberReferenceExpression)expr; + ResolveResult rr = typeVisitor.Resolve(fieldReferenceExpression.TargetObject); + if (rr is NamespaceResolveResult) { + return ((NamespaceResolveResult)rr).Name + "." + fieldReferenceExpression.MemberName; + } + } else if (expr is TypeReferenceExpression) { + return (expr as TypeReferenceExpression).TypeReference.Type; + } + return null; + } + + IClass GetAttribute(string name, NR.Location position) + { + if (name == null) + return null; + IClass c = SearchClass(name, 0, position); + if (c != null) { + if (c.IsTypeInInheritanceTree(c.ProjectContent.SystemTypes.Attribute.GetUnderlyingClass())) + return c; + } + return SearchClass(name + "Attribute", 0, position); + } + + ResolveResult ResolveAttribute(Expression expr, NR.Location position) + { + string attributeName = GetAttributeName(expr); + if (attributeName != null) { + IClass c = GetAttribute(attributeName, position); + if (c != null) { + return new TypeResolveResult(callingClass, callingMember, c); + } else { + string ns = SearchNamespace(attributeName, position); + if (ns != null) { + return new NamespaceResolveResult(callingClass, callingMember, ns); + } + } + return new UnknownIdentifierResolveResult(callingClass, callingMember, attributeName); + } else if (expr is InvocationExpression) { + InvocationExpression ie = (InvocationExpression)expr; + attributeName = GetAttributeName(ie.TargetObject); + IClass c = GetAttribute(attributeName, position); + if (c != null) { + ResolveVisitor resolveVisitor = new ResolveVisitor(this); + return resolveVisitor.ResolveConstructorOverload(c, ie.Arguments); + } + return CreateUnknownMethodResolveResult(expr as InvocationExpression); + } + return null; + } + + internal UnknownMethodResolveResult CreateUnknownMethodResolveResult(InvocationExpression invocationExpression) + { + List arguments = new List(); + ResolveVisitor resolveVisitor = new ResolveVisitor(this); + + foreach (Expression ex in invocationExpression.Arguments) { + ResolveResult rr = resolveVisitor.Resolve(ex); + if (rr != null) + arguments.Add(rr.ResolvedType ?? UnknownReturnType.Instance); + else + arguments.Add(UnknownReturnType.Instance); + } + + IReturnType target = null; + + if (callingClass != null) + target = callingClass.DefaultReturnType; + + string methodName = ""; + bool isStatic = false; + + if (invocationExpression.TargetObject is IdentifierExpression) { + IdentifierExpression ie = invocationExpression.TargetObject as IdentifierExpression; + methodName = ie.Identifier; + isStatic = callingMember != null && callingMember.Modifiers.HasFlag(ModifierEnum.Static); + } + + if (invocationExpression.TargetObject is MemberReferenceExpression) { + MemberReferenceExpression mre = invocationExpression.TargetObject as MemberReferenceExpression; + var rr = resolveVisitor.Resolve(mre.TargetObject); + isStatic = rr is TypeResolveResult; + if (rr != null) + target = rr.ResolvedType; + methodName = mre.MemberName; + } + + return new UnknownMethodResolveResult(callingClass, callingMember, target, methodName, isStatic, arguments); + } + + public ResolveResult ResolveInternal(Expression expr, ExpressionContext context) + { + if (projectContent == null) + throw new InvalidOperationException("Cannot use uninitialized resolver"); + + // we need to special-case this to pass the context to ResolveIdentifier + if (expr is IdentifierExpression) + return ResolveIdentifier(expr as IdentifierExpression, context); + + ResolveVisitor resolveVisitor = new ResolveVisitor(this); + return resolveVisitor.Resolve(expr); + } + + /// + /// Used for the fix for SD2-511. + /// + public int LimitMethodExtractionUntilLine; + + public TextReader ExtractCurrentMethod(string fileContent) + { + if (callingMember == null) + return null; + return ExtractMethod(fileContent, callingMember, language, LimitMethodExtractionUntilLine); + } + + /// + /// Creates a new class containing only the specified member. + /// This is useful because we only want to parse current method for local variables, + /// as all fields etc. are already prepared in the AST. + /// + public static TextReader ExtractMethod(string fileContent, IMember member, + NR.SupportedLanguage language, int extractUntilLine) + { + // As the parse information is always some seconds old, the end line could be wrong + // if the user just inserted a line in the method. + // We can ignore that case because it is sufficient for the parser when the first part of the + // method body is ok. + // Since we are operating directly on the edited buffer, the parser might not be + // able to resolve invalid declarations. + // We can ignore even that because the 'invalid line' is the line the user is currently + // editing, and the declarations he is using are always above that line. + + + // The ExtractMethod-approach has the advantage that the method contents do not have + // do be parsed and stored in memory before they are needed. + // Previous SharpDevelop versions always stored the SharpRefactory[VB] parse tree as 'Tag' + // to the AST CompilationUnit. + // This approach doesn't need that, so one could even go and implement a special parser + // mode that does not parse the method bodies for the normal run (in the ParserUpdateThread or + // SolutionLoadThread). That could improve the parser's speed dramatically. + + if (member.Region.IsEmpty) return null; + int startLine = member.Region.BeginLine; + if (startLine < 1) return null; + DomRegion bodyRegion = member.BodyRegion; + if (bodyRegion.IsEmpty) bodyRegion = member.Region; + int endLine = bodyRegion.EndLine; + + // Fix for SD2-511 (Code completion in inserted line) + if (extractUntilLine > startLine && extractUntilLine < endLine) + endLine = extractUntilLine; + + int offset = 0; + for (int i = 0; i < startLine - 1; ++i) { // -1 because the startLine must be included + offset = fileContent.IndexOf('\n', offset) + 1; + if (offset <= 0) return null; + } + int startOffset = offset; + for (int i = startLine - 1; i < endLine; ++i) { + int newOffset = fileContent.IndexOf('\n', offset) + 1; + if (newOffset <= 0) break; + offset = newOffset; + } + int length = offset - startOffset; + string classDecl, endClassDecl; + if (language == NR.SupportedLanguage.VBNet) { + classDecl = "Class A"; + endClassDecl = "End Class\n"; + } else { + classDecl = "class A {"; + endClassDecl = "}\n"; + } + System.Text.StringBuilder b = new System.Text.StringBuilder(classDecl, length + classDecl.Length + endClassDecl.Length + startLine - 1); + b.Append('\n', startLine - 1); + b.Append(fileContent, startOffset, length); + b.Append(endClassDecl); + return new System.IO.StringReader(b.ToString()); + } + + #region Resolve Identifier + internal IReturnType ConstructType(IReturnType baseType, List typeArguments) + { + if (typeArguments == null || typeArguments.Count == 0) + return baseType; + return new ConstructedReturnType(baseType, + typeArguments.ConvertAll(r => TypeVisitor.CreateReturnType(r, this))); + } + + public ResolveResult ResolveIdentifier(IdentifierExpression expr, ExpressionContext context) + { + ResolveResult result = ResolveIdentifierInternal(expr); + if (result is TypeResolveResult) + return result; + + NR.Location position = expr.StartLocation; + string identifier = expr.Identifier; + ResolveResult result2 = null; + + if (callingClass != null) { + if (callingMember is IMethod) { + foreach (ITypeParameter typeParameter in (callingMember as IMethod).TypeParameters) { + if (IsSameName(identifier, typeParameter.Name)) { + return new TypeResolveResult(callingClass, callingMember, new GenericReturnType(typeParameter)); + } + } + } + foreach (ITypeParameter typeParameter in callingClass.TypeParameters) { + if (IsSameName(identifier, typeParameter.Name)) { + return new TypeResolveResult(callingClass, callingMember, new GenericReturnType(typeParameter)); + } + } + } + + SearchTypeResult typeResult = SearchType(identifier, expr.TypeArguments.Count, position); + IReturnType t = typeResult.Result; + if (t != null) { + result2 = new TypeResolveResult(callingClass, callingMember, ConstructType(t, expr.TypeArguments)); + } else if (result == null && typeResult.NamespaceResult != null) { + return new NamespaceResolveResult(callingClass, callingMember, typeResult.NamespaceResult); + } + + if (result == null && result2 == null) + return new UnknownIdentifierResolveResult(CallingClass, CallingMember, expr.Identifier); + + if (result == null) return result2; + if (result2 == null) return result; + if (context == ExpressionContext.Type) + return result2; + return new MixedResolveResult(result, result2); + } + + public ResolveResult ResolveIdentifier(string identifier, NR.Location position, ExpressionContext context) + { + return ResolveIdentifier(new IdentifierExpression(identifier) { StartLocation = position }, context); + } + + IField CreateLocalVariableField(LocalLookupVariable variable) + { + IReturnType type = GetVariableType(variable); + var f = new DefaultField.LocalVariableField(type, variable.Name, DomRegion.FromLocation(variable.StartPos, variable.EndPos), callingClass); + if (variable.IsConst) { + f.Modifiers |= ModifierEnum.Const; + } + return f; + } + + ResolveResult ResolveIdentifierInternal(IdentifierExpression identifierExpression) + { + NR.Location position = identifierExpression.StartLocation; + string identifier = identifierExpression.Identifier; + if (callingMember != null) { // LocalResolveResult requires callingMember to be set + LocalLookupVariable var = SearchVariable(identifier, position); + if (var != null) { + return new LocalResolveResult(callingMember, CreateLocalVariableField(var)); + } + IParameter para = SearchMethodParameter(identifier); + if (para != null) { + IField field = new DefaultField.ParameterField(para.ReturnType, para.Name, para.Region, callingClass); + return new LocalResolveResult(callingMember, field); + } + if (IsSameName(identifier, "value")) { + IProperty property = callingMember as IProperty; + if (property != null && property.SetterRegion.IsInside(position.Line, position.Column) + || callingMember is IEvent) + { + IField field = new DefaultField.ParameterField(callingMember.ReturnType, "value", callingMember.Region, callingClass); + return new LocalResolveResult(callingMember, field); + } + } + } + if (callingClass != null) { + IClass tmp = callingClass; + do { + ResolveResult rr = ResolveMember(tmp.DefaultReturnType, identifier, + identifierExpression.TypeArguments, + IsInvoked(identifierExpression), + false, true); + if (rr != null && rr.IsValid) + return rr; + // also try to resolve the member in outer classes + tmp = tmp.DeclaringType; + } while (tmp != null); + } + + if (languageProperties.CanImportClasses) { + IUsingScope scope = callingClass != null ? callingClass.UsingScope : cu.UsingScope; + for (; scope != null; scope = scope.Parent) { + foreach (IUsing @using in scope.Usings) { + foreach (string import in @using.Usings) { + IClass c = GetClass(import, 0); + if (c != null) { + ResolveResult rr = ResolveMember(c.DefaultReturnType, identifier, + identifierExpression.TypeArguments, + IsInvoked(identifierExpression), + false, null); + if (rr != null && rr.IsValid) + return rr; + } + } + } + } + } + + if (languageProperties.ImportModules) { + List list = new List(); + CtrlSpaceResolveHelper.AddImportedNamespaceContents(list, cu, callingClass); + List resultMembers = new List(); + foreach (ICompletionEntry o in list) { + IClass c = o as IClass; + if (c != null && IsSameName(identifier, c.Name)) { + return new TypeResolveResult(callingClass, callingMember, c); + } + IMember member = o as IMember; + if (member != null && IsSameName(identifier, member.Name)) { + resultMembers.Add(member); + } + } + return CreateMemberOrMethodGroupResolveResult(null, identifier, new IList[] { resultMembers }, false, null); + } + + return null; + } + #endregion + + private ResolveResult CreateMemberResolveResult(IMember member) + { + if (member == null) return null; + return new MemberResolveResult(callingClass, callingMember, member); + } + + #region ResolveMember + internal ResolveResult ResolveMember(IReturnType declaringType, string memberName, + List typeArguments, bool isInvocation, + bool allowExtensionMethods, bool? isAccessThoughReferenceOfCurrentClass) + { + // in VB everything can be used with invocation syntax (because the same syntax also accesses indexers), + // don't use 'isInvocation'. + if (language == NR.SupportedLanguage.VBNet) + isInvocation = false; + + List> members = MemberLookupHelper.LookupMember(declaringType, memberName, callingClass, languageProperties, isInvocation, isAccessThoughReferenceOfCurrentClass); + List typeArgs = null; + if (members != null && typeArguments != null && typeArguments.Count != 0) { + typeArgs = typeArguments.ConvertAll(r => TypeVisitor.CreateReturnType(r, this)); + + // For all member-groups: + // Remove all non-methods and methods with incorrect type argument count, then + // apply the type arguments to the remaining methods. + members = members.Select(memberGroup => ApplyTypeArgumentsToMethods(memberGroup, typeArgs)) + .Where(memberGroup => memberGroup.Count > 0) // keep only non-empty groups + .ToList(); + } + if (language == NR.SupportedLanguage.VBNet && members != null && members.Count > 0) { + // use the correct casing of the member name + memberName = members[0][0].Name; + } + return CreateMemberOrMethodGroupResolveResult(declaringType, memberName, members, allowExtensionMethods, typeArgs); + } + + static IList ApplyTypeArgumentsToMethods(IList memberGroup, IList typeArgs) + { + if (typeArgs == null || typeArgs.Count == 0) + return memberGroup; + else + return ApplyTypeArgumentsToMethods(memberGroup.OfType(), typeArgs).Select(m=>(IMember)m).ToList(); + } + + static IEnumerable ApplyTypeArgumentsToMethods(IEnumerable methodGroup, IList typeArgs) + { + if (typeArgs == null || typeArgs.Count == 0) + return methodGroup; + // we have type arguments, so: + // - remove all non-methods + // - remove all methods with incorrect type parameter count + // - apply type arguments to remaining methods + return methodGroup + .Where((IMethod m) => m.TypeParameters.Count == typeArgs.Count) + .Select((IMethod originalMethod) => { + IMethod m = (IMethod)originalMethod.CreateSpecializedMember(); + m.ReturnType = ConstructedReturnType.TranslateType(m.ReturnType, typeArgs, true); + for (int j = 0; j < m.Parameters.Count; ++j) { + m.Parameters[j].ReturnType = ConstructedReturnType.TranslateType(m.Parameters[j].ReturnType, typeArgs, true); + } + return m; + }); + } + + /// + /// Creates a MemberResolveResult or MethodGroupResolveResult. + /// + /// The type on which to search the member. + /// The name of the member. + /// The list of members to put into the MethodGroupResolveResult + /// Whether to add extension methods to the resolve result + /// Type arguments to apply to the extension methods + internal ResolveResult CreateMemberOrMethodGroupResolveResult( + IReturnType declaringType, string memberName, IList> members, + bool allowExtensionMethods, IList typeArgs) + { + List methods = new List(); + if (members != null) { + foreach (IList memberGroup in members) { + if (memberGroup != null && memberGroup.Count > 0) { + MethodGroup methodGroup = new MethodGroup(); + foreach (IMember m in memberGroup) { + if (m is IMethod) + methodGroup.Add(m as IMethod); + else + return new MemberResolveResult(callingClass, callingMember, m); + } + methods.Add(methodGroup); + } + } + } + if (allowExtensionMethods == false || declaringType == null) { + if (methods.Count == 0) + return null; + else + return new MethodGroupResolveResult(callingClass, callingMember, + declaringType ?? methods[0][0].DeclaringTypeReference, + memberName, methods, language == SupportedLanguage.VBNet, allowMethodGroupResolveResult) { IsVBNetAddressOf = allowMethodGroupResolveResult && language == SupportedLanguage.VBNet }; + } else { + methods.Add( + new MethodGroup( + new LazyList(() => ApplyTypeArgumentsToMethods(SearchExtensionMethods(memberName), typeArgs).ToList()) + ) + { IsExtensionMethodGroup = true }); + return new MethodGroupResolveResult(callingClass, callingMember, + declaringType, + memberName, methods, language == SupportedLanguage.VBNet, allowMethodGroupResolveResult) { IsVBNetAddressOf = allowMethodGroupResolveResult && language == SupportedLanguage.VBNet }; + } + } + #endregion + + #region Resolve In Object Initializer + ResolveResult ResolveObjectInitializer(string identifier, string fileContent, out bool isCollectionInitializer) + { + foreach (IMember m in ObjectInitializerCtrlSpace(fileContent, out isCollectionInitializer)) { + if (IsSameName(m.Name, identifier)) + return CreateMemberResolveResult(m); + } + return null; + } + + List ObjectInitializerCtrlSpace(string fileContent, out bool isCollectionInitializer) + { + isCollectionInitializer = true; + if (callingMember == null) { + return new List(); + } + CompilationUnit parsedCu = ParseCurrentMemberAsCompilationUnit(fileContent); + if (parsedCu == null) { + return new List(); + } + return ObjectInitializerCtrlSpace(parsedCu, new NR.Location(caretColumn, caretLine), out isCollectionInitializer); + } + + List ObjectInitializerCtrlSpace(CompilationUnit parsedCu, NR.Location location, out bool isCollectionInitializer) + { + FindObjectInitializerExpressionContainingCaretVisitor v = new FindObjectInitializerExpressionContainingCaretVisitor(location); + parsedCu.AcceptVisitor(v, null); + return ObjectInitializerCtrlSpace(v.result, out isCollectionInitializer); + } + + List ObjectInitializerCtrlSpace(CollectionInitializerExpression collectionInitializer, out bool isCollectionInitializer) + { + isCollectionInitializer = true; + List results = new List(); + if (collectionInitializer != null) { + ObjectCreateExpression oce = collectionInitializer.Parent as ObjectCreateExpression; + MemberInitializerExpression mie = collectionInitializer.Parent as MemberInitializerExpression; + if (oce != null && !oce.IsAnonymousType) { + IReturnType resolvedType = TypeVisitor.CreateReturnType(oce.CreateType, this); + ObjectInitializerCtrlSpaceInternal(results, resolvedType, out isCollectionInitializer); + } + else if (mie != null) { + IMember member = ResolveMemberInitializerExpressionInObjectInitializer(mie); + if (member != null) { + ObjectInitializerCtrlSpaceInternal(results, member.ReturnType, out isCollectionInitializer); + } + } + } + return results; + } + + IMember ResolveMemberInitializerExpressionInObjectInitializer(MemberInitializerExpression mie) + { + CollectionInitializerExpression parentCI = mie.Parent as CollectionInitializerExpression; + bool tmp; + return ObjectInitializerCtrlSpace(parentCI, out tmp).Find(m => IsSameName(m.Name, mie.Name)); + } + + void ObjectInitializerCtrlSpaceInternal(List results, IReturnType resolvedType, out bool isCollectionInitializer) + { + isCollectionInitializer = MemberLookupHelper.ConversionExists(resolvedType, new GetClassReturnType(projectContent, "System.Collections.IEnumerable", 0)); + if (resolvedType != null) { + bool isClassInInheritanceTree = false; + if (callingClass != null) + isClassInInheritanceTree = callingClass.IsTypeInInheritanceTree(resolvedType.GetUnderlyingClass()); + foreach (IField f in resolvedType.GetFields()) { + if (languageProperties.ShowMember(f, false) + && f.IsAccessible(callingClass, isClassInInheritanceTree) + && !(f.IsReadonly && IsValueType(f.ReturnType))) + { + results.Add(f); + } + } + foreach (IProperty p in resolvedType.GetProperties()) { + if (languageProperties.ShowMember(p, false) + && p.IsAccessible(callingClass, isClassInInheritanceTree) + && !(p.CanSet == false && IsValueType(p.ReturnType))) + { + results.Add(p); + } + } + } + } + + static bool IsValueType(IReturnType rt) + { + if (rt == null) + return false; + IClass c = rt.GetUnderlyingClass(); + return c != null && (c.ClassType == ClassType.Struct || c.ClassType == ClassType.Enum); + } + + // Finds the inner most CollectionInitializerExpression containing the specified caret position + sealed class FindObjectInitializerExpressionContainingCaretVisitor : AbstractAstVisitor + { + NR.Location caretPosition; + internal CollectionInitializerExpression result; + + public FindObjectInitializerExpressionContainingCaretVisitor(ICSharpCode.NRefactory.Location caretPosition) + { + this.caretPosition = caretPosition; + } + + public override object VisitCollectionInitializerExpression(CollectionInitializerExpression collectionInitializerExpression, object data) + { + base.VisitCollectionInitializerExpression(collectionInitializerExpression, data); + if (result == null + && collectionInitializerExpression.StartLocation <= caretPosition + && collectionInitializerExpression.EndLocation >= caretPosition) + { + result = collectionInitializerExpression; + } + return null; + } + } + #endregion + + Expression SpecialConstructs(string expression) + { + if (language == NR.SupportedLanguage.VBNet) { + // MyBase and MyClass are no expressions, only MyBase.Identifier and MyClass.Identifier + if ("mybase".Equals(expression, StringComparison.InvariantCultureIgnoreCase)) { + return new BaseReferenceExpression(); + } else if ("myclass".Equals(expression, StringComparison.InvariantCultureIgnoreCase)) { + return new ClassReferenceExpression(); + } // Global is handled in Resolve() because we don't need an expression for that + } + return null; + } + + public bool IsSameName(string name1, string name2) + { + return languageProperties.NameComparer.Equals(name1, name2); + } + + bool IsInside(NR.Location between, NR.Location start, NR.Location end) + { + if (between.Y < start.Y || between.Y > end.Y) { + return false; + } + if (between.Y > start.Y) { + if (between.Y < end.Y) { + return true; + } + // between.Y == end.Y + return between.X <= end.X; + } + // between.Y == start.Y + if (between.X < start.X) { + return false; + } + // start is OK and between.Y <= end.Y + return between.Y < end.Y || between.X <= end.X; + } + + IMember GetCallingMember() + { + if (callingClass == null) + return null; + foreach (IMethod method in callingClass.Methods) { + if (method.Region.IsInside(caretLine, caretColumn) || method.BodyRegion.IsInside(caretLine, caretColumn)) { + return method; + } + } + foreach (IProperty property in callingClass.Properties) { + if (property.Region.IsInside(caretLine, caretColumn) || property.BodyRegion.IsInside(caretLine, caretColumn)) { + return property; + } + } + foreach (IEvent ev in callingClass.Events) { + if (ev.Region.IsInside(caretLine, caretColumn) || ev.BodyRegion.IsInside(caretLine, caretColumn)) { + return ev; + } + } + foreach (IField f in callingClass.Fields) { + if (f.Region.IsInside(caretLine, caretColumn) || f.BodyRegion.IsInside(caretLine, caretColumn)) { + return f; + } + } + return null; + } + + /// + /// use the usings to find the correct name of a namespace + /// + public string SearchNamespace(string name, NR.Location position) + { + return SearchType(name, 0, position).NamespaceResult; + } + + public IClass GetClass(string fullName, int typeArgumentCount) + { + return projectContent.GetClass(fullName, typeArgumentCount); + } + + /// + /// use the usings and the name of the namespace to find a class + /// + public IClass SearchClass(string name, int typeArgumentCount, NR.Location position) + { + IReturnType t = SearchType(name, typeArgumentCount, position).Result; + return (t != null) ? t.GetUnderlyingClass() : null; + } + + public SearchTypeResult SearchType(string name, int typeArgumentCount, NR.Location position) + { + if (position.IsEmpty) + return projectContent.SearchType(new SearchTypeRequest(name, typeArgumentCount, callingClass, cu, caretLine, caretColumn)); + else + return projectContent.SearchType(new SearchTypeRequest(name, typeArgumentCount, callingClass, cu, position.Line, position.Column)); + } + + public IList SearchExtensionMethods(string name) + { + List results = new List(); + foreach (IMethodOrProperty m in SearchAllExtensionMethods()) { + if (IsSameName(name, m.Name)) { + results.Add((IMethod)m); + } + } + return results; + } + + ReadOnlyCollection cachedExtensionMethods; + IClass cachedExtensionMethods_LastClass; // invalidate cache when callingClass != LastClass + + public ReadOnlyCollection SearchAllExtensionMethods(bool searchInAllNamespaces = false) + { + if (callingClass == null) + return EmptyList.Instance; + if (callingClass != cachedExtensionMethods_LastClass) { + cachedExtensionMethods_LastClass = callingClass; + cachedExtensionMethods = new ReadOnlyCollection(CtrlSpaceResolveHelper.FindAllExtensions(languageProperties, callingClass, searchInAllNamespaces)); + } + return cachedExtensionMethods; + } + + #region DynamicLookup + IParameter SearchMethodParameter(string parameter) + { + IMethodOrProperty method = callingMember as IMethodOrProperty; + if (method == null) { + return null; + } + foreach (IParameter p in method.Parameters) { + if (IsSameName(p.Name, parameter)) { + return p; + } + } + return null; + } + + Dictionary variableReturnTypeCache = new Dictionary(); + + IReturnType GetVariableType(LocalLookupVariable v) + { + if (v == null) { + return null; + } + + if (v.ParentLambdaExpression != null && (v.TypeRef == null || v.TypeRef.IsNull)) { + IReturnType[] lambdaParameterTypes = GetImplicitLambdaParameterTypes(v.ParentLambdaExpression); + if (lambdaParameterTypes != null) { + int parameterIndex = v.ParentLambdaExpression.Parameters.FindIndex(p => p.ParameterName == v.Name); + if (parameterIndex >= 0 && parameterIndex < lambdaParameterTypes.Length) { + return lambdaParameterTypes[parameterIndex]; + } + } + } + + // Don't create multiple IReturnType's for the same local variable. + // This is required to ensure that the protection against infinite recursion + // for type inference cycles in InferredReturnType works correctly. + // It also helps performance if we infer every local variable only once. + IReturnType rt; + if (variableReturnTypeCache.TryGetValue(v, out rt)) + return rt; + + if (v.TypeRef == null || v.TypeRef.IsNull || v.TypeRef.Type == "var") { + if (inferAllowed) { + if (v.ParentLambdaExpression != null) { + rt = new LambdaParameterReturnType(v.ParentLambdaExpression, v.Name, this); + } else { + rt = new InferredReturnType(v.Initializer, this); + if (v.IsLoopVariable) { + rt = new ElementReturnType(this.projectContent, rt); + } + } + } else { + rt = this.projectContent.SystemTypes.Object; + } + } else { + rt = TypeVisitor.CreateReturnType(v.TypeRef, this); + } + variableReturnTypeCache[v] = rt; + return rt; + } + + LocalLookupVariable SearchVariable(string name, NR.Location position) + { + if (lookupTableVisitor == null || !lookupTableVisitor.Variables.ContainsKey(name)) + return null; + List variables = lookupTableVisitor.Variables[name]; + if (variables.Count <= 0) { + return null; + } + + foreach (LocalLookupVariable v in variables) { + if (IsInside(position, v.StartPos, v.EndPos)) { + return v; + } + } + return null; + } + #endregion + + IClass GetPrimitiveClass(string systemType, string newName) + { + IClass c = projectContent.GetClass(systemType, 0); + if (c == null) + return null; + DefaultClass c2 = new DefaultClass(c.CompilationUnit, newName); + c2.ClassType = c.ClassType; + c2.Modifiers = c.Modifiers; + c2.Documentation = c.Documentation; + c2.BaseTypes.AddRange(c.BaseTypes); + c2.Methods.AddRange(c.Methods); + c2.Fields.AddRange(c.Fields); + c2.Properties.AddRange(c.Properties); + c2.Events.AddRange(c.Events); + return c2; + } + + static void AddCSharpKeywords(List ar, BitArray keywords) + { + for (int i = 0; i < keywords.Length; i++) { + if (keywords[i]) { + ar.Add(new KeywordEntry(NR.Parser.CSharp.Tokens.GetTokenString(i))); + } + } + } + + /// + /// Returns code completion entries for given context. + /// + /// + /// + /// + /// + /// + /// If true, returns entries from all namespaces, regardless of current imports. + /// + public List CtrlSpace(int caretLine, int caretColumn, ParseInformation parseInfo, string fileContent, ExpressionContext context, bool showEntriesFromAllNamespaces = false) + { + if (!Initialize(parseInfo, caretLine, caretColumn)) + return null; + + List result = new List(); + if (language == NR.SupportedLanguage.VBNet) { + CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); + } else { + if (context == ExpressionContext.TypeDeclaration) { + AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.TypeLevel); + AddCSharpPrimitiveTypes(result); + CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); + } else if (context == CSharpExpressionContext.InterfaceDeclaration) { + AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.InterfaceLevel); + AddCSharpPrimitiveTypes(result); + CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); + } else if (context == ExpressionContext.MethodBody) { + result.Add(new KeywordEntry("var")); + AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.StatementStart); + AddCSharpPrimitiveTypes(result); + CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); + } else if (context == ExpressionContext.Global) { + AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.GlobalLevel); + } else if (context == CSharpExpressionContext.InterfacePropertyDeclaration) { + result.Add(new KeywordEntry("get")); + result.Add(new KeywordEntry("set")); + } else if (context == CSharpExpressionContext.BaseConstructorCall) { + result.Add(new KeywordEntry("this")); + result.Add(new KeywordEntry("base")); + } else if (context == CSharpExpressionContext.ConstraintsStart) { + result.Add(new KeywordEntry("where")); + } else if (context == CSharpExpressionContext.Constraints) { + result.Add(new KeywordEntry("where")); + result.Add(new KeywordEntry("new")); + result.Add(new KeywordEntry("struct")); + result.Add(new KeywordEntry("class")); + AddCSharpPrimitiveTypes(result); + CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); + } else if (context == ExpressionContext.InheritableType) { + result.Add(new KeywordEntry("where")); // the inheritance list can be followed by constraints + AddCSharpPrimitiveTypes(result); + CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); + } else if (context == CSharpExpressionContext.PropertyDeclaration) { + AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.InPropertyDeclaration); + } else if (context == CSharpExpressionContext.EventDeclaration) { + AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.InEventDeclaration); + } else if (context == CSharpExpressionContext.FullyQualifiedType) { + cu.ProjectContent.AddNamespaceContents(result, "", languageProperties, true); + } else if (context == CSharpExpressionContext.ParameterType || context == CSharpExpressionContext.FirstParameterType) { + result.Add(new KeywordEntry("ref")); + result.Add(new KeywordEntry("out")); + result.Add(new KeywordEntry("params")); + if (context == CSharpExpressionContext.FirstParameterType && languageProperties.SupportsExtensionMethods) { + if (callingMember != null && callingMember.IsStatic) { + result.Add(new KeywordEntry("this")); + } + } + AddCSharpPrimitiveTypes(result); + CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); + } else if (context == CSharpExpressionContext.ObjectInitializer) { + bool isCollectionInitializer; + result.AddRange(ObjectInitializerCtrlSpace(fileContent, out isCollectionInitializer)); + if (isCollectionInitializer) { + AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.ExpressionStart); + AddCSharpPrimitiveTypes(result); + CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); + } + } else if (context == ExpressionContext.Attribute) { + CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); + result.Add(new KeywordEntry("assembly")); + result.Add(new KeywordEntry("module")); + result.Add(new KeywordEntry("field")); + result.Add(new KeywordEntry("event")); + result.Add(new KeywordEntry("method")); + result.Add(new KeywordEntry("param")); + result.Add(new KeywordEntry("property")); + result.Add(new KeywordEntry("return")); + result.Add(new KeywordEntry("type")); + } else if (context == ExpressionContext.Default) { + AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.ExpressionStart); + AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.ExpressionContent); + AddCSharpPrimitiveTypes(result); + CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); + } else { + // e.g. some ExpressionContext.TypeDerivingFrom() + AddCSharpPrimitiveTypes(result); + CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); + } + } + return result; + } + + void AddCSharpPrimitiveTypes(List result) + { + foreach (KeyValuePair pair in TypeReference.PrimitiveTypesCSharp) { + IClass c = GetPrimitiveClass(pair.Value, pair.Key); + if (c != null) result.Add(c); + } + } + + void CtrlSpaceInternal(List result, string fileContent, bool showEntriesFromAllNamespaces) + { + lookupTableVisitor = new LookupTableVisitor(language); + + if (callingMember != null) { + CompilationUnit parsedCu = ParseCurrentMemberAsCompilationUnit(fileContent); + if (parsedCu != null) { + lookupTableVisitor.VisitCompilationUnit(parsedCu, null); + } + } + + CtrlSpaceResolveHelper.AddContentsFromCalling(result, callingClass, callingMember); + + foreach (KeyValuePair> pair in lookupTableVisitor.Variables) { + if (pair.Value != null && pair.Value.Count > 0) { + foreach (LocalLookupVariable v in pair.Value) { + if (IsInside(new NR.Location(caretColumn, caretLine), v.StartPos, v.EndPos)) { + // convert to a field for display + result.Add(CreateLocalVariableField(v)); + break; + } + } + } + } + if (callingMember is IProperty) { + IProperty property = (IProperty)callingMember; + if (property.SetterRegion.IsInside(caretLine, caretColumn)) { + result.Add(new DefaultField.ParameterField(property.ReturnType, "value", property.Region, callingClass)); + } + } + if (callingMember is IEvent) { + result.Add(new DefaultField.ParameterField(callingMember.ReturnType, "value", callingMember.Region, callingClass)); + } + + if (showEntriesFromAllNamespaces) { + // CC contains contents of all referenced assemblies + CtrlSpaceResolveHelper.AddReferencedProjectsContents(result, cu, callingClass); + } else { + // CC contains contents of all imported namespaces + CtrlSpaceResolveHelper.AddImportedNamespaceContents(result, cu, callingClass); + } + } + + sealed class CompareLambdaByLocation : IEqualityComparer + { + public bool Equals(LambdaExpression x, LambdaExpression y) + { + return x.StartLocation == y.StartLocation && x.EndLocation == y.EndLocation; + } + + public int GetHashCode(LambdaExpression obj) + { + return unchecked (8123351 * obj.StartLocation.GetHashCode() + obj.EndLocation.GetHashCode()); + } + } + + Dictionary lambdaParameterTypes = new Dictionary(new CompareLambdaByLocation()); + + internal void SetImplicitLambdaParameterTypes(LambdaExpression lambda, IReturnType[] types) + { + lambdaParameterTypes[lambda] = types; + } + + internal void UnsetImplicitLambdaParameterTypes(LambdaExpression lambda) + { + lambdaParameterTypes.Remove(lambda); + } + + IReturnType[] GetImplicitLambdaParameterTypes(LambdaExpression lambda) + { + IReturnType[] types; + if (lambdaParameterTypes.TryGetValue(lambda, out types)) + return types; + else + return null; + } + + internal static bool IsInvoked(Expression expr) + { + InvocationExpression ie = expr.Parent as InvocationExpression; + if (ie != null) { + return ie.TargetObject == expr; + } + return false; + } + + public IReturnType GetExpectedTypeFromContext(Expression expr) + { + if (expr == null) + return null; + + InvocationExpression ie = expr.Parent as InvocationExpression; + if (ie != null) { + int index = ie.Arguments.IndexOf(expr); + if (index < 0) + return null; + + ResolveResult rr = ResolveInternal(ie, ExpressionContext.Default); + IMethod m; + if (rr is MemberResolveResult) { + m = (rr as MemberResolveResult).ResolvedMember as IMethod; + if ((rr as MemberResolveResult).IsExtensionMethodCall) + index++; + } else if (rr is DelegateCallResolveResult) { + m = (rr as DelegateCallResolveResult).DelegateInvokeMethod; + } else { + m = null; + } + if (m != null && index < m.Parameters.Count) + return m.Parameters[index].ReturnType; + } + + ObjectCreateExpression oce = expr.Parent as ObjectCreateExpression; + if (oce != null) { + int index = oce.Parameters.IndexOf(expr); + if (index < 0) + return null; + + MemberResolveResult mrr = ResolveInternal(oce, ExpressionContext.Default) as MemberResolveResult; + if (mrr != null) { + IMethod m = mrr.ResolvedMember as IMethod; + if (m != null && index < m.Parameters.Count) + return m.Parameters[index].ReturnType; + } + } + + if (expr.Parent is CastExpression) { + ResolveResult rr = ResolveInternal((Expression)expr.Parent, ExpressionContext.Default); + if (rr != null) + return rr.ResolvedType; + } else if (expr.Parent is LambdaExpression) { + IReturnType delegateType = GetExpectedTypeFromContext(expr.Parent as Expression); + IMethod sig = CSharp.TypeInference.GetDelegateOrExpressionTreeSignature(delegateType, true); + if (sig != null) + return sig.ReturnType; + } else if (expr.Parent is ReturnStatement) { + Expression lambda = GetParentAnonymousMethodOrLambda(expr.Parent); + if (lambda != null) { + IReturnType delegateType = GetExpectedTypeFromContext(lambda); + IMethod sig = CSharp.TypeInference.GetDelegateOrExpressionTreeSignature(delegateType, true); + if (sig != null) + return sig.ReturnType; + } else { + if (callingMember != null) + return callingMember.ReturnType; + } + } else if (expr.Parent is ParenthesizedExpression) { + return GetExpectedTypeFromContext(expr.Parent as Expression); + } else if (expr.Parent is VariableDeclaration) { + return GetTypeFromVariableDeclaration((VariableDeclaration)expr.Parent); + } else if (expr.Parent is AssignmentExpression) { + ResolveResult rr = ResolveInternal((expr.Parent as AssignmentExpression).Left, ExpressionContext.Default); + if (rr != null) + return rr.ResolvedType; + } else if (expr.Parent is MemberInitializerExpression) { + IMember m = ResolveMemberInitializerExpressionInObjectInitializer((MemberInitializerExpression)expr.Parent); + if (m != null) + return m.ReturnType; + } else if (expr.Parent is CollectionInitializerExpression) { + IReturnType collectionType; + if (expr.Parent.Parent is ObjectCreateExpression) + collectionType = TypeVisitor.CreateReturnType(((ObjectCreateExpression)expr.Parent.Parent).CreateType, this); + else if (expr.Parent.Parent is ArrayCreateExpression) + collectionType = TypeVisitor.CreateReturnType(((ArrayCreateExpression)expr.Parent.Parent).CreateType, this); + else if (expr.Parent.Parent is VariableDeclaration) + collectionType = GetTypeFromVariableDeclaration((VariableDeclaration)expr.Parent.Parent); + else + collectionType = null; + if (collectionType != null) + return new ElementReturnType(projectContent, collectionType); + } else if (expr.Parent is IndexerExpression) { + IndexerExpression indexerExpr = expr.Parent as IndexerExpression; + MemberResolveResult indexer = ResolveInternal(indexerExpr, ExpressionContext.Default) + as MemberResolveResult; + + if (indexer != null && indexer.ResolvedMember is IProperty) { + IProperty p = indexer.ResolvedMember as IProperty; + int position = indexerExpr.Indexes.IndexOf(expr); + if (position < 0 || position >= p.Parameters.Count) + return null; + return p.Parameters[position].ReturnType; + } + } + return null; + } + + Expression GetParentAnonymousMethodOrLambda(INode node) + { + while (node != null) { + if (node is AnonymousMethodExpression || node is LambdaExpression) + return (Expression)node; + node = node.Parent; + } + return null; + } + + IReturnType GetTypeFromVariableDeclaration(VariableDeclaration varDecl) + { + TypeReference typeRef = varDecl.TypeReference; + if (typeRef == null || typeRef.IsNull) { + LocalVariableDeclaration lvd = varDecl.Parent as LocalVariableDeclaration; + if (lvd != null) typeRef = lvd.TypeReference; + FieldDeclaration fd = varDecl.Parent as FieldDeclaration; + if (fd != null) typeRef = fd.TypeReference; + } + return TypeVisitor.CreateReturnType(typeRef, this); + } + } + + /// + /// Used in CtrlSpace-results to represent a keyword. + /// + public class KeywordEntry : ICompletionEntry + { + public string Name { get; private set; } + + public KeywordEntry(string name) + { + if (name == null) + throw new ArgumentNullException("name"); + this.Name = name; + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override bool Equals(object obj) + { + KeywordEntry e = obj as KeywordEntry; + return e != null && e.Name == this.Name; + } + + public override string ToString() + { + return Name; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs new file mode 100644 index 000000000..11b1187a8 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs @@ -0,0 +1,751 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Runtime.Remoting.Messaging; +using System.Text; +using System.Linq; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.Visitors; + +namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver +{ + /// + /// Resolves expressions. + /// + sealed class ResolveVisitor : AbstractAstVisitor + { + NRefactoryResolver resolver; + + public ResolveVisitor(NRefactoryResolver resolver) + { + this.resolver = resolver; + } + + ResolveResult CreateResolveResult(TypeReference reference) + { + return CreateResolveResult(TypeVisitor.CreateReturnType(reference, resolver)); + } + + ResolveResult CreateResolveResult(Expression expression) + { + return CreateResolveResult(ResolveType(expression)); + } + + ResolveResult CreateResolveResult(IReturnType resolvedType) + { + if (resolvedType == null) + return null; + else + return new ResolveResult(resolver.CallingClass, resolver.CallingMember, resolvedType); + } + + TypeResolveResult CreateTypeResolveResult(IReturnType resolvedType) + { + if (resolvedType == null) { + return null; + } else { + IReturnType rt = resolvedType; + while (rt != null && rt.IsArrayReturnType) { + rt = rt.CastToArrayReturnType().ArrayElementType; + } + IClass resolvedClass = rt != null ? rt.GetUnderlyingClass() : null; + return new TypeResolveResult(resolver.CallingClass, resolver.CallingMember, resolvedType, resolvedClass); + } + } + + MemberResolveResult CreateMemberResolveResult(IMember member) + { + if (member == null) + return null; + else + return new MemberResolveResult(resolver.CallingClass, resolver.CallingMember, member); + } + + readonly Dictionary cachedResults = new Dictionary(); + + public ResolveResult Resolve(Expression expression) + { + ResolveResult rr; + if (!cachedResults.TryGetValue(expression, out rr)) { + rr = (ResolveResult)expression.AcceptVisitor(this, null); + if (rr != null) + rr.Freeze(); + cachedResults[expression] = rr; + } + return rr; + } + + public IReturnType ResolveType(Expression expression) + { + ResolveResult rr = Resolve(expression); + if (rr != null) + return rr.ResolvedType; + else + return null; + } + + public override object VisitAddressOfExpression(AddressOfExpression addressOfExpression, object data) + { + bool oldValue = resolver.allowMethodGroupResolveResult; + resolver.allowMethodGroupResolveResult = true; + object result = base.VisitAddressOfExpression(addressOfExpression, data); + resolver.allowMethodGroupResolveResult = oldValue; + return result; + } + + public override object VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, object data) + { + return CreateResolveResult(new LambdaReturnType(anonymousMethodExpression, resolver)); + } + + public override object VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, object data) + { + if (arrayCreateExpression.IsImplicitlyTyped) { + return CreateResolveResult(arrayCreateExpression.ArrayInitializer); + } else { + return CreateTypeResolveResult(TypeVisitor.CreateReturnType(arrayCreateExpression.CreateType, resolver)); + } + } + + public override object VisitAssignmentExpression(AssignmentExpression assignmentExpression, object data) + { + return CreateResolveResult(assignmentExpression.Left); + } + + public override object VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression, object data) + { + if (resolver.CallingClass == null) { + return null; + } + if (resolver.Language == SupportedLanguage.VBNet && IsInstanceConstructor(resolver.CallingMember)) { + return new VBBaseOrThisReferenceInConstructorResolveResult( + resolver.CallingClass, resolver.CallingMember, resolver.CallingClass.BaseType); + } else { + return new BaseResolveResult(resolver.CallingClass, resolver.CallingMember, resolver.CallingClass.BaseType); + } + } + + public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data) + { + switch (binaryOperatorExpression.Op) { + case BinaryOperatorType.NullCoalescing: + return CreateResolveResult(binaryOperatorExpression.Right); + case BinaryOperatorType.DivideInteger: + return CreateResolveResult(resolver.ProjectContent.SystemTypes.Int32); + case BinaryOperatorType.Concat: + return CreateResolveResult(resolver.ProjectContent.SystemTypes.String); + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + case BinaryOperatorType.ReferenceEquality: + case BinaryOperatorType.ReferenceInequality: + case BinaryOperatorType.LogicalAnd: + case BinaryOperatorType.LogicalOr: + case BinaryOperatorType.LessThan: + case BinaryOperatorType.LessThanOrEqual: + case BinaryOperatorType.GreaterThan: + case BinaryOperatorType.GreaterThanOrEqual: + return CreateResolveResult(resolver.ProjectContent.SystemTypes.Boolean); + default: + return CreateResolveResult(MemberLookupHelper.GetCommonType( + resolver.ProjectContent, + ResolveType(binaryOperatorExpression.Left), + ResolveType(binaryOperatorExpression.Right))); + } + } + + public override object VisitCastExpression(CastExpression castExpression, object data) + { + return CreateResolveResult(castExpression.CastTo); + } + + public override object VisitCheckedExpression(CheckedExpression checkedExpression, object data) + { + return CreateResolveResult(checkedExpression.Expression); + } + + public override object VisitClassReferenceExpression(ClassReferenceExpression classReferenceExpression, object data) + { + if (resolver.CallingClass != null) + return CreateResolveResult(resolver.CallingClass.DefaultReturnType); + else + return null; + } + + public override object VisitCollectionInitializerExpression(CollectionInitializerExpression collectionInitializerExpression, object data) + { + // used for implicitly typed arrays + if (collectionInitializerExpression.CreateExpressions.Count == 0) + return null; + IReturnType combinedRT = ResolveType(collectionInitializerExpression.CreateExpressions[0]); + for (int i = 1; i < collectionInitializerExpression.CreateExpressions.Count; i++) { + IReturnType rt = ResolveType(collectionInitializerExpression.CreateExpressions[i]); + combinedRT = MemberLookupHelper.GetCommonType(resolver.ProjectContent, combinedRT, rt); + } + if (combinedRT == null) + return null; + return CreateResolveResult(new ArrayReturnType(resolver.ProjectContent, combinedRT, 1)); + } + + public override object VisitConditionalExpression(ConditionalExpression conditionalExpression, object data) + { + return CreateResolveResult(MemberLookupHelper.GetCommonType( + resolver.ProjectContent, + ResolveType(conditionalExpression.TrueExpression), + ResolveType(conditionalExpression.FalseExpression))); + } + + public override object VisitConstructorInitializer(ConstructorInitializer constructorInitializer, object data) + { + return null; + } + + public override object VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, object data) + { + return CreateResolveResult(defaultValueExpression.TypeReference); + } + + public override object VisitDirectionExpression(DirectionExpression directionExpression, object data) + { + return CreateResolveResult(new ReferenceReturnType(ResolveType(directionExpression.Expression))); + } + + public override object VisitIdentifierExpression(IdentifierExpression identifierExpression, object data) + { + return resolver.ResolveIdentifier(identifierExpression, ExpressionContext.Default); + } + + public override object VisitIndexerExpression(IndexerExpression indexerExpression, object data) + { + IReturnType target = ResolveType(indexerExpression.TargetObject); + return CreateMemberResolveResult( + GetIndexer(target, indexerExpression.Indexes) + ); + } + + IProperty GetIndexer(IReturnType target, List indexes) + { + if (target == null) + return null; + return MemberLookupHelper.FindOverload( + target.GetProperties().Where((IProperty p) => p.IsIndexer).ToList(), + indexes.Select(ResolveType).ToArray() + ); + } + + IProperty GetVisualBasicIndexer(InvocationExpression invocationExpression) + { + ResolveResult targetRR = Resolve(invocationExpression.TargetObject); + if (targetRR != null) { + // Visual Basic can call indexers in two ways: + // collection(index) - use indexer + // collection.Item(index) - use parametrized property + + if (invocationExpression.TargetObject is IdentifierExpression || invocationExpression.TargetObject is MemberReferenceExpression) { + // only IdentifierExpression/MemberReferenceExpression can represent a parametrized property + // - the check is necessary because collection.Items and collection.Item(index) both + // resolve to the same property, but we want to use the default indexer for the second call in + // collection.Item(index1)(index2) + MemberResolveResult memberRR = targetRR as MemberResolveResult; + if (memberRR != null) { + IProperty p = memberRR.ResolvedMember as IProperty; + if (p != null && p.Parameters.Count > 0) { + // this is a parametrized property + return p; + } + } + } + // not a parametrized property - try normal indexer + return GetIndexer(targetRR.ResolvedType, invocationExpression.Arguments); + } else { + return null; + } + } + + public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data) + { + if (resolver.Language == SupportedLanguage.CSharp && resolver.CallingClass != null) { + if (invocationExpression.TargetObject is ThisReferenceExpression) { + // call to constructor + return ResolveConstructorOverload(resolver.CallingClass, invocationExpression.Arguments); + } else if (invocationExpression.TargetObject is BaseReferenceExpression) { + return ResolveConstructorOverload(resolver.CallingClass.BaseType, invocationExpression.Arguments); + } + } + + ResolveResult rr = Resolve(invocationExpression.TargetObject); + MixedResolveResult mixedRR = rr as MixedResolveResult; + if (mixedRR != null) { + rr = mixedRR.PrimaryResult; + } + MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult; + if (mgrr != null) { + if (resolver.Language == SupportedLanguage.VBNet) { + if (mgrr.Methods.All(mg => mg.Count == 0)) + return CreateMemberResolveResult(GetVisualBasicIndexer(invocationExpression)); +// IMethod empty = mgrr.GetMethodWithEmptyParameterList(); +// if (empty != null) +// return CreateMemberResolveResult(empty); + } + + IReturnType[] argumentTypes = invocationExpression.Arguments.Select(ResolveType).ToArray(); + + MemberResolveResult firstResult = null; + foreach (MethodGroup methodGroup in mgrr.Methods) { + bool resultIsAcceptable; + IMethod method; + if (methodGroup.IsExtensionMethodGroup) { + IReturnType[] extendedTypes = new IReturnType[argumentTypes.Length + 1]; + extendedTypes[0] = mgrr.ContainingType; + argumentTypes.CopyTo(extendedTypes, 1); + method = MemberLookupHelper.FindOverload(methodGroup, extendedTypes, out resultIsAcceptable); + } else { + method = MemberLookupHelper.FindOverload(methodGroup, argumentTypes, out resultIsAcceptable); + } + MemberResolveResult result = CreateMemberResolveResult(method); + if (result != null && methodGroup.IsExtensionMethodGroup) + result.IsExtensionMethodCall = true; + if (resultIsAcceptable) + return result; + if (firstResult == null) + firstResult = result; + } + if (firstResult != null) { + return firstResult; + } else { + return FallbackResolveMethod(invocationExpression, mgrr, argumentTypes); + } + } else if (rr != null && rr.ResolvedType != null) { + IClass c = rr.ResolvedType.GetUnderlyingClass(); + if (c != null && c.ClassType == ClassType.Delegate) { + // We don't want to show "System.EventHandler.Invoke" in the tooltip + // of "EventCall(this, EventArgs.Empty)", we just show the event/delegate for now + // but for DelegateCall(params).* completion, we use the delegate's + // return type instead of the delegate type itself + + IMethod method = rr.ResolvedType.GetMethods().FirstOrDefault(innerMethod => innerMethod.Name == "Invoke"); + if (method != null) { + return new DelegateCallResolveResult(rr, method); + } + } + } + if (resolver.Language == SupportedLanguage.VBNet) { + return CreateMemberResolveResult(GetVisualBasicIndexer(invocationExpression)); + } + + return resolver.CreateUnknownMethodResolveResult(invocationExpression); + } + + ResolveResult FallbackResolveMethod(InvocationExpression invocation, MethodGroupResolveResult mgrr, IReturnType[] argumentTypes) + { + // method not found, let's try if we can find a method if we violate the + // accessibility rules + MemberReferenceExpression mre = invocation.TargetObject as MemberReferenceExpression; + if (mre != null) { + List methods = mgrr.ContainingType.GetMethods().Where(m => resolver.IsSameName(m.Name, mre.MemberName)).ToList(); + bool resultIsAcceptable; + IMethod result = MemberLookupHelper.FindOverload( + methods, argumentTypes, out resultIsAcceptable); + if (result != null) { + return CreateMemberResolveResult(result); + } + } + + return resolver.CreateUnknownMethodResolveResult(invocation); + } + + public override object VisitLambdaExpression(LambdaExpression lambdaExpression, object data) + { + return CreateResolveResult(new LambdaReturnType(lambdaExpression, resolver)); + } + + public override object VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data) + { + IReturnType type; + if (string.IsNullOrEmpty(memberReferenceExpression.MemberName)) { + // NRefactory creates this "dummy" fieldReferenceExpression when it should + // parse a primitive type name (int, short; Integer, Decimal) + if (memberReferenceExpression.TargetObject is TypeReferenceExpression) { + type = TypeVisitor.CreateReturnType(((TypeReferenceExpression)memberReferenceExpression.TargetObject).TypeReference, resolver); + return CreateTypeResolveResult(type); + } + } + ResolveResult targetRR = Resolve(memberReferenceExpression.TargetObject); + if (targetRR == null) + return null; + + type = GetType(targetRR); + if (targetRR is NamespaceResolveResult) { + return ResolveMemberInNamespace(((NamespaceResolveResult)targetRR).Name, memberReferenceExpression); + } else if (type != null) { + TypeResolveResult typeRR = targetRR as TypeResolveResult; + if (typeRR != null && typeRR.ResolvedClass != null) { + foreach (IClass c1 in typeRR.ResolvedClass.ClassInheritanceTree) { + foreach (IClass c in c1.InnerClasses) { + if (resolver.IsSameName(memberReferenceExpression.MemberName, c.Name) + && c.TypeParameters.Count == memberReferenceExpression.TypeArguments.Count) + { + return CreateTypeResolveResult(resolver.ConstructType(c.DefaultReturnType, memberReferenceExpression.TypeArguments)); + } + } + } + } + + var memberRR = resolver.ResolveMember(type, memberReferenceExpression.MemberName, + memberReferenceExpression.TypeArguments, + NRefactoryResolver.IsInvoked(memberReferenceExpression), + typeRR == null, // allow extension methods only for non-static method calls + targetRR is BaseResolveResult ? (bool?)true : null // allow calling protected members using "base." + ); + +// MethodGroupResolveResult mgRR = memberRR as MethodGroupResolveResult; +// +// if (mgRR == null) +// mgRR = targetRR as MethodGroupResolveResult; +// +// if (mgRR != null && !resolver.allowMethodGroupResolveResult) +// return CreateMemberResolveResult(mgRR.GetMethodWithEmptyParameterList()); + + return memberRR; + } + return null; + } + + IReturnType GetType(ResolveResult targetRR) + { + if (targetRR.ResolvedType != null) + return targetRR.ResolvedType; + + if (targetRR is MixedResolveResult && ((MixedResolveResult)targetRR).TypeResult != null) + return ((MixedResolveResult)targetRR).TypeResult.ResolvedType; + + return null; + } + + ResolveResult ResolveMemberInNamespace(string namespaceName, MemberReferenceExpression mre) + { + string combinedName; + if (string.IsNullOrEmpty(namespaceName)) + combinedName = mre.MemberName; + else + combinedName = namespaceName + "." + mre.MemberName; + if (resolver.ProjectContent.NamespaceExists(combinedName)) { + return new NamespaceResolveResult(resolver.CallingClass, resolver.CallingMember, combinedName); + } + IClass c = resolver.GetClass(combinedName, mre.TypeArguments.Count); + if (c != null) { + return CreateTypeResolveResult(resolver.ConstructType(c.DefaultReturnType, mre.TypeArguments)); + } + if (resolver.LanguageProperties.ImportModules) { + // go through the members of the modules + List possibleMembers = new List(); + foreach (object o in resolver.ProjectContent.GetNamespaceContents(namespaceName)) { + IMember member = o as IMember; + if (member != null && resolver.IsSameName(member.Name, mre.MemberName)) { + possibleMembers.Add(member); + } + } + return resolver.CreateMemberOrMethodGroupResolveResult( + null, mre.MemberName, new IList[] { possibleMembers }, false, null); + } + return null; + } + + public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data) + { + if (objectCreateExpression.IsAnonymousType) { + return CreateResolveResult(CreateAnonymousTypeClass(objectCreateExpression.ObjectInitializer).DefaultReturnType); + } else { + IReturnType rt = TypeVisitor.CreateReturnType(objectCreateExpression.CreateType, resolver); + if (rt == null) + return new UnknownConstructorCallResolveResult(resolver.CallingClass, resolver.CallingMember, objectCreateExpression.CreateType.ToString()); + + return ResolveConstructorOverload(rt, objectCreateExpression.Parameters) + ?? CreateResolveResult(rt); + } + } + + internal ResolveResult ResolveConstructorOverload(IReturnType rt, List arguments) + { + if (rt == null) + return null; + + List methods = rt.GetMethods().Where(m => m.IsConstructor && !m.IsStatic).ToList(); + IReturnType[] argumentTypes = arguments.Select(ResolveType).ToArray(); + bool resultIsAcceptable; + IMethod result = MemberLookupHelper.FindOverload(methods, argumentTypes, out resultIsAcceptable); + + ResolveResult rr = CreateMemberResolveResult(result); + if (rr != null) + rr.ResolvedType = rt; + return rr; + } + + internal ResolveResult ResolveConstructorOverload(IClass c, List arguments) + { + if (c == null) + return null; + else + return ResolveConstructorOverload(c.DefaultReturnType, arguments); + } + + DefaultClass CreateAnonymousTypeClass(CollectionInitializerExpression initializer) + { + List fieldTypes = new List(); + List fieldNames = new List(); + + foreach (Expression expr in initializer.CreateExpressions) { + if (expr is NamedArgumentExpression) { + // use right part only + fieldTypes.Add( ResolveType(((NamedArgumentExpression)expr).Expression) ); + } else { + fieldTypes.Add( ResolveType(expr) ); + } + + fieldNames.Add(GetAnonymousTypeFieldName(expr)); + } + + StringBuilder nameBuilder = new StringBuilder(); + nameBuilder.Append('{'); + for (int i = 0; i < fieldTypes.Count; i++) { + if (i > 0) nameBuilder.Append(", "); + nameBuilder.Append(fieldNames[i]); + nameBuilder.Append(" : "); + if (fieldTypes[i] != null) { + nameBuilder.Append(fieldTypes[i].DotNetName); + } + } + nameBuilder.Append('}'); + + DefaultClass c = new DefaultClass(new DefaultCompilationUnit(resolver.ProjectContent), nameBuilder.ToString()); + c.Modifiers = ModifierEnum.Internal | ModifierEnum.Synthetic | ModifierEnum.Sealed; + for (int i = 0; i < fieldTypes.Count; i++) { + DefaultProperty p = new DefaultProperty(fieldNames[i], fieldTypes[i], ModifierEnum.Public | ModifierEnum.Synthetic, DomRegion.Empty, DomRegion.Empty, c); + p.CanGet = true; + p.CanSet = false; + c.Properties.Add(p); + } + return c; + } + + static string GetAnonymousTypeFieldName(Expression expr) + { + if (expr is MemberReferenceExpression) { + return ((MemberReferenceExpression)expr).MemberName; + } else if (expr is NamedArgumentExpression) { + return ((NamedArgumentExpression)expr).Name; + } else if (expr is IdentifierExpression) { + return ((IdentifierExpression)expr).Identifier; + } else { + return "?"; + } + } + + public override object VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, object data) + { + return CreateResolveResult(parenthesizedExpression.Expression); + } + + public override object VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, object data) + { + ResolveResult targetRR = Resolve(pointerReferenceExpression.TargetObject); + if (targetRR == null || targetRR.ResolvedType == null) + return null; + PointerReturnType type = targetRR.ResolvedType.CastToDecoratingReturnType(); + if (type != null) { + return resolver.ResolveMember(type.BaseType, pointerReferenceExpression.MemberName, + pointerReferenceExpression.TypeArguments, + NRefactoryResolver.IsInvoked(pointerReferenceExpression), + true, null + ); + } + return null; + } + + public override object VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, object data) + { + if (primitiveExpression.Value == null) { + return CreateResolveResult(NullReturnType.Instance); + } else if (primitiveExpression.Value is int) { + return new IntegerLiteralResolveResult(resolver.CallingClass, resolver.CallingMember, resolver.ProjectContent.SystemTypes.Int32); + } else { + return CreateResolveResult(resolver.ProjectContent.SystemTypes.CreatePrimitive(primitiveExpression.Value.GetType())); + } + } + + public override object VisitQueryExpression(QueryExpression queryExpression, object data) + { + QueryExpressionSelectClause selectClause = queryExpression.SelectOrGroupClause as QueryExpressionSelectClause; + QueryExpressionGroupClause groupClause = queryExpression.SelectOrGroupClause as QueryExpressionGroupClause; + if (selectClause != null) { + // Fake a call to 'Select' + var fakeInvocation = new InvocationExpression(new MemberReferenceExpression( + queryExpression.FromClause.Sources.First().Expression, "Select")); + + var selector = new LambdaExpression(); + selector.Parameters.Add(new ParameterDeclarationExpression(null, "__rangeVariable")); + selector.ExpressionBody = selectClause.Projection; + selector.Parent = fakeInvocation; + + fakeInvocation.Arguments.Add(selector); + + return CreateResolveResult(ResolveType(fakeInvocation)); + } else if (groupClause != null) { + // Fake a call to 'GroupBy' + var fakeInvocation = new InvocationExpression(new MemberReferenceExpression( + queryExpression.FromClause.Sources.First().Expression, "GroupBy")); + + var keySelector = new LambdaExpression(); + keySelector.Parameters.Add(new ParameterDeclarationExpression(null, "__rangeVariable")); + keySelector.ExpressionBody = groupClause.GroupBy; + keySelector.Parent = fakeInvocation; + + var elementSelector = new LambdaExpression(); + elementSelector.Parameters.Add(new ParameterDeclarationExpression(null, "__rangeVariable")); + elementSelector.ExpressionBody = groupClause.Projection; + elementSelector.Parent = fakeInvocation; + + fakeInvocation.Arguments.Add(keySelector); + fakeInvocation.Arguments.Add(elementSelector); + + return CreateResolveResult(ResolveType(fakeInvocation)); + } else { + return null; + } + } + + public override object VisitSizeOfExpression(SizeOfExpression sizeOfExpression, object data) + { + return CreateResolveResult(resolver.ProjectContent.SystemTypes.Int32); + } + + public override object VisitStackAllocExpression(StackAllocExpression stackAllocExpression, object data) + { + return null; + } + + public override object VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, object data) + { + if (resolver.CallingClass == null) + return null; + if (resolver.Language == SupportedLanguage.VBNet && IsInstanceConstructor(resolver.CallingMember)) { + return new VBBaseOrThisReferenceInConstructorResolveResult( + resolver.CallingClass, resolver.CallingMember, resolver.CallingClass.DefaultReturnType); + } else { + return CreateResolveResult(resolver.CallingClass.DefaultReturnType); + } + } + + static bool IsInstanceConstructor(IMember member) + { + IMethod m = member as IMethod; + return m != null && m.IsConstructor && !m.IsStatic; + } + + public override object VisitTypeOfExpression(TypeOfExpression typeOfExpression, object data) + { + return CreateResolveResult(resolver.ProjectContent.SystemTypes.Type); + } + + public override object VisitTypeOfIsExpression(TypeOfIsExpression typeOfIsExpression, object data) + { + return CreateResolveResult(resolver.ProjectContent.SystemTypes.Boolean); + } + + public override object VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data) + { + TypeReference reference = typeReferenceExpression.TypeReference; + ResolveResult rr = CreateTypeResolveResult(TypeVisitor.CreateReturnType(reference, resolver)); + if (rr == null && reference.GenericTypes.Count == 0 && !reference.IsArrayType) { + // reference to namespace is possible + if (reference.IsGlobal) { + if (resolver.ProjectContent.NamespaceExists(reference.Type)) + return new NamespaceResolveResult(resolver.CallingClass, resolver.CallingMember, reference.Type); + } else { + string name = resolver.SearchNamespace(reference.Type, typeReferenceExpression.StartLocation); + if (name != null) + return new NamespaceResolveResult(resolver.CallingClass, resolver.CallingMember, name); + } + } + if (rr != null) { + return rr; + } else { + return new UnknownIdentifierResolveResult(resolver.CallingClass, resolver.CallingMember, reference.Type); + } + } + + public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data) + { + IReturnType type = ResolveType(unaryOperatorExpression.Expression); + if (type == null) + return null; + switch (unaryOperatorExpression.Op) { + case UnaryOperatorType.AddressOf: + return CreateResolveResult(new PointerReturnType(type)); + case UnaryOperatorType.Dereference: + PointerReturnType prt = type.CastToDecoratingReturnType(); + if (prt != null) { + return CreateResolveResult(prt.BaseType); + } else { + return null; + } + default: + return CreateResolveResult(type); + } + } + + public override object VisitUncheckedExpression(UncheckedExpression uncheckedExpression, object data) + { + return CreateResolveResult(uncheckedExpression.Expression); + } + + #region XML Literal resolver + public override object VisitXmlContentExpression(XmlContentExpression xmlContentExpression, object data) + { + switch (xmlContentExpression.Type) { + case XmlContentType.Comment: + return CreateResolveResult(new TypeReference("System.Xml.Linq.XComment")); + case XmlContentType.Text: + return CreateResolveResult(new TypeReference("System.Xml.Linq.XText")); + case XmlContentType.CData: + return CreateResolveResult(new TypeReference("System.Xml.Linq.XCData")); + case XmlContentType.ProcessingInstruction: + if (xmlContentExpression.Content.StartsWith("xml ", StringComparison.OrdinalIgnoreCase)) + return CreateResolveResult(new TypeReference("System.Xml.Linq.XDocumentType")); + return CreateResolveResult(new TypeReference("System.Xml.Linq.XProcessingInstruction")); + default: + throw new Exception("Invalid value for XmlContentType"); + } + } + + public override object VisitXmlDocumentExpression(XmlDocumentExpression xmlDocumentExpression, object data) + { + return CreateResolveResult(new TypeReference("System.Xml.Linq.XDocument")); + } + + public override object VisitXmlElementExpression(XmlElementExpression xmlElementExpression, object data) + { + return CreateResolveResult(new TypeReference("System.Xml.Linq.XElement")); + } + + public override object VisitXmlMemberAccessExpression(XmlMemberAccessExpression xmlMemberAccessExpression, object data) + { + switch (xmlMemberAccessExpression.AxisType) { + case XmlAxisType.Element: + case XmlAxisType.Descendents: + return CreateResolveResult( + new TypeReference("System.Collections.Generic.IEnumerable", + new List { new TypeReference("System.Xml.Linq.XElement") { IsGlobal = true } } + ) { IsGlobal = true } + ); + case XmlAxisType.Attribute: + return CreateResolveResult(new TypeReference("System.String", true) { IsGlobal = true }); + default: + throw new Exception("Invalid value for XmlAxisType"); + } + } + #endregion + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/TypeVisitor.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/TypeVisitor.cs new file mode 100644 index 000000000..073189a63 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/TypeVisitor.cs @@ -0,0 +1,130 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +// created on 22.08.2003 at 19:02 + +using System; +using System.Collections.Generic; +using System.Text; + +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.Visitors; + +namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver +{ + // TODO: Rename this class, the visitor functionality was moved to ResolveVisitor + public static class TypeVisitor + { + [Flags] + public enum ReturnTypeOptions + { + None = 0, + Lazy = 1, + BaseTypeReference = 2 + } + + public static IReturnType CreateReturnType(TypeReference reference, NRefactoryResolver resolver) + { + return CreateReturnType(reference, + resolver.CallingClass, resolver.CallingMember, + resolver.CaretLine, resolver.CaretColumn, + resolver.ProjectContent, ReturnTypeOptions.None); + } + + public static IReturnType CreateReturnType(TypeReference reference, IClass callingClass, + IMember callingMember, int caretLine, int caretColumn, + IProjectContent projectContent, + ReturnTypeOptions options) + { + if (reference == null) return null; + if (reference.IsNull) return null; + if (reference is InnerClassTypeReference) { + reference = ((InnerClassTypeReference)reference).CombineToNormalTypeReference(); + } + + bool useLazyReturnType = (options & ReturnTypeOptions.Lazy) == ReturnTypeOptions.Lazy; + bool isBaseTypeReference = (options & ReturnTypeOptions.BaseTypeReference) == ReturnTypeOptions.BaseTypeReference; + + LanguageProperties languageProperties = projectContent.Language; + IReturnType t = null; + if (callingClass != null && !reference.IsGlobal) { + foreach (ITypeParameter tp in callingClass.TypeParameters) { + if (languageProperties.NameComparer.Equals(tp.Name, reference.Type)) { + t = new GenericReturnType(tp); + break; + } + } + IMethod callingMethod = callingMember as IMethod; + if (t == null && callingMethod != null) { + foreach (ITypeParameter tp in callingMethod.TypeParameters) { + if (languageProperties.NameComparer.Equals(tp.Name, reference.Type)) { + t = new GenericReturnType(tp); + break; + } + } + } + } + if (t == null && reference.Type == "dynamic") { + t = new DynamicReturnType(projectContent); + } + if (t == null) { + int typeParameterCount = reference.GenericTypes.Count; + if (reference.IsKeyword) { + // keyword-type like void, int, string etc. + IClass c = projectContent.GetClass(reference.Type, typeParameterCount); + if (c != null) + t = c.DefaultReturnType; + else + t = new GetClassReturnType(projectContent, reference.Type, typeParameterCount); + } else { + if (useLazyReturnType || isBaseTypeReference) { + if (reference.IsGlobal) { + t = new GetClassReturnType(projectContent, reference.Type, typeParameterCount); + } else if (callingClass != null) { + SearchClassReturnType scrt = new SearchClassReturnType(projectContent, callingClass, caretLine, caretColumn, reference.Type, typeParameterCount); + if (isBaseTypeReference) + scrt.LookForInnerClassesInDeclaringClass = false; + t = scrt; + } + } else { + IClass c; + if (reference.IsGlobal) { + c = projectContent.GetClass(reference.Type, typeParameterCount); + t = (c != null) ? c.DefaultReturnType : null; + } else if (callingClass != null) { + t = projectContent.SearchType(new SearchTypeRequest(reference.Type, typeParameterCount, callingClass, caretLine, caretColumn)).Result; + } + if (t == null) { + return null; + } + } + } + } + if (reference.GenericTypes.Count > 0) { + IReturnType[] para = new IReturnType[reference.GenericTypes.Count]; + for (int i = 0; i < reference.GenericTypes.Count; ++i) { + para[i] = CreateReturnType(reference.GenericTypes[i], callingClass, callingMember, caretLine, caretColumn, projectContent, options); + } + t = new ConstructedReturnType(t, para); + } + for (int i = 0; i < reference.PointerNestingLevel; i++) { + t = new PointerReturnType(t); + } + return WrapArray(projectContent, t, reference); + } + + static IReturnType WrapArray(IProjectContent pc, IReturnType t, TypeReference reference) + { + if (reference.IsArrayType) { + for (int i = reference.RankSpecifier.Length - 1; i >= 0; --i) { + int dimensions = reference.RankSpecifier[i] + 1; + if (dimensions > 0) { + t = new ArrayReturnType(pc, t, dimensions); + } + } + } + return t; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/VBNetToCSharpConvertVisitor.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/VBNetToCSharpConvertVisitor.cs new file mode 100644 index 000000000..f685b217d --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/VBNetToCSharpConvertVisitor.cs @@ -0,0 +1,703 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.AstBuilder; +using ICSharpCode.NRefactory.Visitors; + +namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver +{ + /// + /// This class converts C# constructs to their VB.NET equivalents. + /// + public class VBNetToCSharpConvertVisitor : VBNetConstructsConvertVisitor + { + // Fixes identifier casing + // Adds using statements for the default usings + // Convert "ReDim" statement + // Convert "WithEvents" fields/"Handles" clauses + // Insert InitializeComponents() call into default constructor + + public string NamespacePrefixToAdd { get; set; } + + protected readonly IProjectContent projectContent; + protected readonly NRefactoryResolver resolver; + protected readonly ParseInformation parseInformation; + + public VBNetToCSharpConvertVisitor(IProjectContent pc, ParseInformation parseInfo) + { + this.resolver = new NRefactoryResolver(LanguageProperties.VBNet); + this.projectContent = pc; + this.parseInformation = parseInfo; + } + + public override object VisitCompilationUnit(CompilationUnit compilationUnit, object data) + { + base.VisitCompilationUnit(compilationUnit, data); + if (!string.IsNullOrEmpty(NamespacePrefixToAdd)) { + for (int i = 0; i < compilationUnit.Children.Count; i++) { + NamespaceDeclaration ns = compilationUnit.Children[i] as NamespaceDeclaration; + if (ns != null) { + ns.Name = NamespacePrefixToAdd + "." + ns.Name; + } + if (compilationUnit.Children[i] is TypeDeclaration || compilationUnit.Children[i] is DelegateDeclaration) { + ns = new NamespaceDeclaration(NamespacePrefixToAdd); + ns.AddChild(compilationUnit.Children[i]); + compilationUnit.Children[i] = ns; + } + } + } + + ToCSharpConvertVisitor v = new ToCSharpConvertVisitor(); + compilationUnit.AcceptVisitor(v, data); + if (projectContent != null && projectContent.DefaultImports != null) { + int index = 0; + foreach (string u in projectContent.DefaultImports.Usings) { + compilationUnit.Children.Insert(index++, new UsingDeclaration(u)); + } + } + return null; + } + + public override object VisitUsing(Using @using, object data) + { + base.VisitUsing(@using, data); + if (projectContent != null && projectContent.DefaultImports != null) { + if (!@using.IsAlias) { + // remove using if it is already part of the project-wide imports + foreach (string defaultImport in projectContent.DefaultImports.Usings) { + if (resolver.IsSameName(defaultImport, @using.Name)) { + RemoveCurrentNode(); + break; + } + } + } + } + return null; + } + + public override object VisitUsingDeclaration(UsingDeclaration usingDeclaration, object data) + { + base.VisitUsingDeclaration(usingDeclaration, data); + if (usingDeclaration.Usings.Count == 0) { + RemoveCurrentNode(); + } + return null; + } + + public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + { + resolver.Initialize(parseInformation, typeDeclaration.BodyStartLocation.Line, typeDeclaration.BodyStartLocation.Column); + + if (resolver.CallingClass != null) { + // add Partial modifier to all parts of the class + IClass callingClass = resolver.CallingClass.GetCompoundClass(); + if (callingClass.IsPartial) { + typeDeclaration.Modifier |= Modifiers.Partial; + } + // determine if the type contains handles clauses referring to the current type + bool containsClassHandlesClauses = false; + bool hasConstructors = false; + foreach (IMethod method in callingClass.Methods) { + // do not count compiler-generated constructors + if (method.IsSynthetic) continue; + + hasConstructors |= method.IsConstructor; + foreach (string handles in method.HandlesClauses) { + containsClassHandlesClauses |= !handles.Contains("."); + } + } + // ensure the type has at least one constructor to which the AddHandlerStatements can be added + CompoundClass compoundClass = callingClass as CompoundClass; + if (!hasConstructors) { + // add constructor only to one part + if (compoundClass == null || compoundClass.Parts[0] == resolver.CallingClass) { + if (containsClassHandlesClauses || RequiresConstructor(callingClass)) { + AddDefaultConstructor(callingClass, typeDeclaration); + } + } + } + } + + base.VisitTypeDeclaration(typeDeclaration, data); + return null; + } + + /// + /// Gets if the converter should add a default constructor to the current class if the + /// class does not have any constructors. + /// + protected virtual bool RequiresConstructor(IClass currentClass) + { + // the VB compiler automatically adds the InitializeComponents() call to the + // default constructor, so the converter has to an explicit constructor + // and place the call there + return IsAutomaticallyCallingInitializeComponent(currentClass); + } + + bool IsAutomaticallyCallingInitializeComponent(IClass currentClass) + { + if (currentClass != null) { + if (currentClass.SearchMember("InitializeComponent", LanguageProperties.VBNet) is IMethod) { + foreach (IAttribute at in currentClass.Attributes) { + if (at.AttributeType.FullyQualifiedName == "Microsoft.VisualBasic.CompilerServices.DesignerGeneratedAttribute") { + return true; + } + } + } + } + return false; + } + + /// + /// Adds a default constructor to the type. + /// + protected virtual ConstructorDeclaration AddDefaultConstructor(IClass currentClass, TypeDeclaration typeDeclaration) + { + ConstructorDeclaration cd = new ConstructorDeclaration(typeDeclaration.Name, Modifiers.Public, null, null); + cd.Body = new BlockStatement(); + // location is required to make Resolve() work in the constructor + cd.StartLocation = cd.Body.StartLocation = cd.EndLocation = cd.Body.EndLocation = typeDeclaration.BodyStartLocation; + typeDeclaration.AddChild(cd); + if (IsAutomaticallyCallingInitializeComponent(currentClass)) { + // the VB compiler automatically adds the InitializeComponents() call to the + // default constructor, so the converter has to add the call when creating an explicit + // constructor + cd.Body.AddStatement(new IdentifierExpression("InitializeComponent").Call()); + } + return cd; + } + + public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) + { + // Initialize resolver for method: + if (!methodDeclaration.Body.IsNull) { + if (resolver.Initialize(parseInformation, methodDeclaration.Body.StartLocation.Line, methodDeclaration.Body.StartLocation.Column)) { + resolver.RunLookupTableVisitor(methodDeclaration); + } + } + + methodDeclaration.HandlesClause.Clear(); + + return base.VisitMethodDeclaration(methodDeclaration, data); + } + + public override object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data) + { + resolver.Initialize(parseInformation, fieldDeclaration.StartLocation.Line, fieldDeclaration.StartLocation.Column); + + base.VisitFieldDeclaration(fieldDeclaration, data); + + if ((fieldDeclaration.Modifier & Modifiers.WithEvents) == Modifiers.WithEvents) { + TransformWithEventsField(fieldDeclaration); + if (fieldDeclaration.Fields.Count == 0) { + RemoveCurrentNode(); + } + } + + return null; + } + + void TransformWithEventsField(FieldDeclaration fieldDeclaration) + { + if (resolver.CallingClass == null) + return; + + INode insertAfter = fieldDeclaration; + + for (int i = 0; i < fieldDeclaration.Fields.Count;) { + VariableDeclaration field = fieldDeclaration.Fields[i]; + + IdentifierExpression backingFieldNameExpression = null; + PropertyDeclaration createdProperty = null; + foreach (IMethod m in resolver.CallingClass.GetCompoundClass().Methods) { + foreach (string handlesClause in m.HandlesClauses) { + int pos = handlesClause.IndexOf('.'); + if (pos > 0) { + string fieldName = handlesClause.Substring(0, pos); + string eventName = handlesClause.Substring(pos + 1); + if (resolver.IsSameName(fieldName, field.Name)) { + if (createdProperty == null) { + FieldDeclaration backingField = new FieldDeclaration(null); + backingField.Fields.Add(new VariableDeclaration( + "withEventsField_" + field.Name, field.Initializer, fieldDeclaration.GetTypeForField(i))); + backingField.Modifier = Modifiers.Private; + InsertAfterSibling(insertAfter, backingField); + createdProperty = new PropertyDeclaration(fieldDeclaration.Modifier, null, field.Name, null); + createdProperty.TypeReference = fieldDeclaration.GetTypeForField(i); + createdProperty.StartLocation = fieldDeclaration.StartLocation; + createdProperty.EndLocation = fieldDeclaration.EndLocation; + + backingFieldNameExpression = new IdentifierExpression(backingField.Fields[0].Name); + + createdProperty.GetRegion = new PropertyGetRegion(new BlockStatement(), null); + createdProperty.GetRegion.Block.AddChild(new ReturnStatement( + backingFieldNameExpression)); + + Expression backingFieldNotNullTest = new BinaryOperatorExpression( + backingFieldNameExpression, + BinaryOperatorType.InEquality, + new PrimitiveExpression(null, "null")); + + createdProperty.SetRegion = new PropertySetRegion(new BlockStatement(), null); + createdProperty.SetRegion.Block.AddChild(new IfElseStatement( + backingFieldNotNullTest, new BlockStatement() + )); + createdProperty.SetRegion.Block.AddChild(new ExpressionStatement( + new AssignmentExpression( + backingFieldNameExpression, + AssignmentOperatorType.Assign, + new IdentifierExpression("value")))); + createdProperty.SetRegion.Block.AddChild(new IfElseStatement( + backingFieldNotNullTest, new BlockStatement() + )); + InsertAfterSibling(backingField, createdProperty); + insertAfter = createdProperty; + } + + // insert code to remove the event handler + IfElseStatement ies = (IfElseStatement)createdProperty.SetRegion.Block.Children[0]; + ies.TrueStatement[0].AddChild(new RemoveHandlerStatement( + new MemberReferenceExpression(backingFieldNameExpression, eventName), + new AddressOfExpression(new IdentifierExpression(m.Name)))); + + // insert code to add the event handler + ies = (IfElseStatement)createdProperty.SetRegion.Block.Children[2]; + ies.TrueStatement[0].AddChild(new AddHandlerStatement( + new MemberReferenceExpression(backingFieldNameExpression, eventName), + new AddressOfExpression(new IdentifierExpression(m.Name)))); + } + } + } + } + + if (createdProperty != null) { + // field replaced with property + fieldDeclaration.Fields.RemoveAt(i); + } else { + i++; + } + } + } + + public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) + { + if (!constructorDeclaration.Body.IsNull) { + if (resolver.Initialize(parseInformation, constructorDeclaration.Body.StartLocation.Line, constructorDeclaration.Body.StartLocation.Column)) { + resolver.RunLookupTableVisitor(constructorDeclaration); + } + } + base.VisitConstructorDeclaration(constructorDeclaration, data); + if (resolver.CallingClass != null) { + if (constructorDeclaration.ConstructorInitializer.IsNull + || constructorDeclaration.ConstructorInitializer.ConstructorInitializerType != ConstructorInitializerType.This) + { + AddClassEventHandlersToConstructor(constructorDeclaration); + } + } + return null; + } + + void AddClassEventHandlersToConstructor(ConstructorDeclaration constructorDeclaration) + { + foreach (IMethod method in resolver.CallingClass.GetCompoundClass().Methods) { + foreach (string handles in method.HandlesClauses) { + if (!handles.Contains(".")) { + AddHandlerStatement ahs = new AddHandlerStatement( + new IdentifierExpression(handles), + new AddressOfExpression(new IdentifierExpression(method.Name)) + ); + constructorDeclaration.Body.Children.Insert(0, ahs); + ahs.Parent = constructorDeclaration.Body; + } + } + } + } + + public override object VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data) + { + if (resolver.Initialize(parseInformation, propertyDeclaration.BodyStart.Line, propertyDeclaration.BodyStart.Column)) { + resolver.RunLookupTableVisitor(propertyDeclaration); + } + return base.VisitPropertyDeclaration(propertyDeclaration, data); + } + + public override object VisitIdentifierExpression(IdentifierExpression identifierExpression, object data) + { + base.VisitIdentifierExpression(identifierExpression, data); + if (resolver.CompilationUnit == null) + return null; + + ResolveResult rr = resolver.ResolveInternal(identifierExpression, ExpressionContext.Default); + string ident = GetIdentifierFromResult(rr); + if (ident != null) { + identifierExpression.Identifier = ident; + } + + if (ReplaceWithInvocation(identifierExpression, rr)) {} + else if (FullyQualifyModuleMemberReference(identifierExpression, rr)) {} + else if (FullyQualifyNamespaceReference(identifierExpression, rr)) {} + + return null; + } + + public override object VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data) + { + base.VisitMemberReferenceExpression(memberReferenceExpression, data); + + if (resolver.CompilationUnit == null) + return null; + + ResolveResult rr = Resolve(memberReferenceExpression); + string ident = GetIdentifierFromResult(rr); + if (ident != null) { + memberReferenceExpression.MemberName = ident; + } + if (ReplaceWithInvocation(memberReferenceExpression, rr)) {} + else if (FullyQualifyModuleMemberReference(memberReferenceExpression, rr)) {} + + return rr; + } + + protected bool IsReferenceToInstanceMember(ResolveResult rr) + { + MemberResolveResult memberRR = rr as MemberResolveResult; + if (memberRR != null) + return memberRR.ResolvedMember.IsStatic == false; + MethodGroupResolveResult methodRR = rr as MethodGroupResolveResult; + if (methodRR != null && methodRR.ContainingType != null) { + foreach (IMethod m in methodRR.ContainingType.GetMethods()) { + if (resolver.IsSameName(m.Name, methodRR.Name)) { + return !m.IsStatic; + } + } + } + return false; + } + + bool ReplaceWithInvocation(Expression expression, ResolveResult rr) + { + // replace with invocation if rr is a method + // and were not taking the address and it's not already being invoked + MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult; + if (mgrr != null + && mgrr.Methods.Any(g=>g.Count>0) + && !(expression.Parent is AddressOfExpression) + && !(NRefactoryResolver.IsInvoked(expression))) + { + InvocationExpression ie = new InvocationExpression(expression); + ReplaceCurrentNode(ie); + return true; + } + return false; + } + + IReturnType GetContainingTypeOfStaticMember(ResolveResult rr) + { + MethodGroupResolveResult methodRR = rr as MethodGroupResolveResult; + if (methodRR != null) { + return methodRR.ContainingType; + } + MemberResolveResult memberRR = rr as MemberResolveResult; + if (memberRR != null && memberRR.ResolvedMember.IsStatic) { + return memberRR.ResolvedMember.DeclaringTypeReference; + } + return null; + } + + bool FullyQualifyModuleMemberReference(IdentifierExpression ident, ResolveResult rr) + { + IReturnType containingType = GetContainingTypeOfStaticMember(rr); + if (containingType == null) + return false; + if (resolver.CallingClass != null) { + if (resolver.CallingClass.IsTypeInInheritanceTree(containingType.GetUnderlyingClass())) + return false; + } + ReplaceCurrentNode(new MemberReferenceExpression( + new TypeReferenceExpression(ConvertType(containingType)), + ident.Identifier + )); + return true; + } + + bool FullyQualifyNamespaceReference(IdentifierExpression ident, ResolveResult rr) + { + NamespaceResolveResult nrr = rr as NamespaceResolveResult; + if (nrr == null) + return false; + if (nrr.Name.IndexOf('.') >= 0) { + // simple identifier points to complex namespace + ReplaceCurrentNode(MakeFieldReferenceExpression(nrr.Name)); + } else { + ident.Identifier = nrr.Name; + } + return true; + } + + TypeReference ConvertType(IReturnType type) + { + return Refactoring.CodeGenerator.ConvertType(type, CreateContext()); + } + + bool FullyQualifyModuleMemberReference(MemberReferenceExpression mre, ResolveResult rr) + { + IReturnType containingType = GetContainingTypeOfStaticMember(rr); + if (containingType == null) + return false; + + ResolveResult targetRR = resolver.ResolveInternal(mre.TargetObject, ExpressionContext.Default); + if (targetRR is NamespaceResolveResult) { + mre.TargetObject = new TypeReferenceExpression(ConvertType(containingType)); + return true; + } + return false; + } + + public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data) + { + base.VisitInvocationExpression(invocationExpression, data); + + if (resolver.CompilationUnit == null) + return null; + + if (!(invocationExpression.Parent is ReDimStatement)) + { + ProcessInvocationExpression(invocationExpression); + } + + return null; + } + + protected ResolveResult Resolve(Expression expression) + { + if (resolver.CompilationUnit == null) { + return null; + } else { + return resolver.ResolveInternal(expression, ExpressionContext.Default); + } + } + + void ProcessInvocationExpression(InvocationExpression invocationExpression) + { + MemberResolveResult rr = Resolve(invocationExpression) as MemberResolveResult; + if (rr != null) { + IProperty p = rr.ResolvedMember as IProperty; + if (p != null && invocationExpression.Arguments.Count > 0) { + // col(i) -> col[i] or col.Items(i) -> col[i] ? + Expression targetObject = invocationExpression.TargetObject; + MemberReferenceExpression targetObjectFre = targetObject as MemberReferenceExpression; + if (p.IsIndexer && targetObjectFre != null) { + MemberResolveResult rr2 = Resolve(targetObjectFre) as MemberResolveResult; + if (rr2 != null && rr2.ResolvedMember.FullyQualifiedName == rr.ResolvedMember.FullyQualifiedName) { + // remove ".Items" + targetObject = targetObjectFre.TargetObject; + } + } + ReplaceCurrentNode(new IndexerExpression(targetObject, invocationExpression.Arguments)); + } + IMethod m = rr.ResolvedMember as IMethod; + if (m != null && invocationExpression.Arguments.Count == m.Parameters.Count) { + for (int i = 0; i < m.Parameters.Count; i++) { + if (m.Parameters[i].IsOut) { + invocationExpression.Arguments[i] = new DirectionExpression( + FieldDirection.Out, invocationExpression.Arguments[i]); + } else if (m.Parameters[i].IsRef) { + invocationExpression.Arguments[i] = new DirectionExpression( + FieldDirection.Ref, invocationExpression.Arguments[i]); + } + } + } + } + } + + ClassFinder CreateContext() + { + return new ClassFinder(resolver.CallingClass, resolver.CallingMember, resolver.CaretLine, resolver.CaretColumn); + } + + public override object VisitReDimStatement(ReDimStatement reDimStatement, object data) + { + base.VisitReDimStatement(reDimStatement, data); + + if (resolver.CompilationUnit == null) + return null; + + if (reDimStatement.ReDimClauses.Count != 1) + return null; + + if (reDimStatement.IsPreserve) { + if (reDimStatement.ReDimClauses[0].Arguments.Count > 1) { + // multidimensional Redim Preserve + // replace with: + // MyArray = (int[,])Microsoft.VisualBasic.CompilerServices.Utils.CopyArray(MyArray, new int[dim1+1, dim2+1]); + + ResolveResult rr = Resolve(reDimStatement.ReDimClauses[0].TargetObject); + if (rr != null && rr.ResolvedType != null && rr.ResolvedType.IsArrayReturnType) { + ArrayCreateExpression ace = new ArrayCreateExpression(ConvertType(rr.ResolvedType)); + foreach (Expression arg in reDimStatement.ReDimClauses[0].Arguments) { + ace.Arguments.Add(Expression.AddInteger(arg, 1)); + } + + ReplaceCurrentNode(new ExpressionStatement( + new AssignmentExpression( + reDimStatement.ReDimClauses[0].TargetObject, + AssignmentOperatorType.Assign, + new CastExpression( + ace.CreateType, + new InvocationExpression( + MakeFieldReferenceExpression("Microsoft.VisualBasic.CompilerServices.Utils.CopyArray"), + new List { + reDimStatement.ReDimClauses[0].TargetObject, + ace + } + ), + CastType.Cast + ) + ))); + } + } + } else { + // replace with array create expression + + ResolveResult rr = Resolve(reDimStatement.ReDimClauses[0].TargetObject); + if (rr != null && rr.ResolvedType != null && rr.ResolvedType.IsArrayReturnType) { + ArrayCreateExpression ace = new ArrayCreateExpression(ConvertType(rr.ResolvedType)); + foreach (Expression arg in reDimStatement.ReDimClauses[0].Arguments) { + ace.Arguments.Add(Expression.AddInteger(arg, 1)); + } + + ReplaceCurrentNode(new ExpressionStatement( + new AssignmentExpression(reDimStatement.ReDimClauses[0].TargetObject, AssignmentOperatorType.Assign, ace))); + } + } + return null; + } + + protected Expression MakeFieldReferenceExpression(string name) + { + Expression e = null; + foreach (string n in name.Split('.')) { + if (e == null) + e = new IdentifierExpression(n); + else + e = new MemberReferenceExpression(e, n); + } + return e; + } + + public override object VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, object data) + { + base.VisitDefaultValueExpression(defaultValueExpression, data); + + IReturnType type = FixTypeReferenceCasing(defaultValueExpression.TypeReference, defaultValueExpression.StartLocation); + // the VBNetConstructsConvertVisitor will initialize local variables to + // default(TypeReference). + // MyType m = null; looks nicer than MyType m = default(MyType)) + // so we replace default(ReferenceType) with null + if (type != null && type.IsReferenceType == true) { + ReplaceCurrentNode(new PrimitiveExpression(null, "null")); + } + return null; + } + + public override object VisitVariableDeclaration(VariableDeclaration variableDeclaration, object data) + { + FixTypeReferenceCasing(variableDeclaration.TypeReference, variableDeclaration.StartLocation); + return base.VisitVariableDeclaration(variableDeclaration, data); + } + + public override object VisitParameterDeclarationExpression(ParameterDeclarationExpression parameterDeclarationExpression, object data) + { + FixTypeReferenceCasing(parameterDeclarationExpression.TypeReference, parameterDeclarationExpression.StartLocation); + return base.VisitParameterDeclarationExpression(parameterDeclarationExpression, data); + } + + public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data) + { + FixTypeReferenceCasing(objectCreateExpression.CreateType, objectCreateExpression.StartLocation); + return base.VisitObjectCreateExpression(objectCreateExpression, data); + } + + IReturnType FixTypeReferenceCasing(TypeReference tr, Location loc) + { + if (resolver.CompilationUnit == null) return null; + if (tr.IsNull) return null; + var searchTypeResult = resolver.SearchType(tr.Type, tr.GenericTypes.Count, loc); + IReturnType rt = searchTypeResult.Result; + if (rt != null) { + IClass c = rt.GetUnderlyingClass(); + if (c != null) { + if (string.Equals(tr.Type, c.Name, StringComparison.OrdinalIgnoreCase)) { + tr.Type = c.Name; + } else if (string.Equals(tr.Type, c.FullyQualifiedName, StringComparison.OrdinalIgnoreCase)) { + tr.Type = c.FullyQualifiedName; + } else if (searchTypeResult.UsedUsing != null && !searchTypeResult.UsedUsing.HasAliases) { + tr.Type = c.FullyQualifiedName; + } + } + } + foreach (TypeReference arg in tr.GenericTypes) { + FixTypeReferenceCasing(arg, loc); + } + return rt; + } + + string GetIdentifierFromResult(ResolveResult rr) + { + LocalResolveResult lrr = rr as LocalResolveResult; + if (lrr != null) + return lrr.VariableName; + MemberResolveResult mrr = rr as MemberResolveResult; + if (mrr != null) + return mrr.ResolvedMember.Name; + MethodGroupResolveResult mtrr = rr as MethodGroupResolveResult; + if (mtrr != null) + return mtrr.Name; + TypeResolveResult trr = rr as TypeResolveResult; + if (trr != null && trr.ResolvedClass != null) + return trr.ResolvedClass.Name; + return null; + } + + public override object VisitForeachStatement(ForeachStatement foreachStatement, object data) + { + base.VisitForeachStatement(foreachStatement, data); + + FixTypeReferenceCasing(foreachStatement.TypeReference, foreachStatement.StartLocation); + + if (resolver.CompilationUnit == null) + return null; + + if (foreachStatement.TypeReference.IsNull) { + ResolveResult rr = resolver.ResolveIdentifier(foreachStatement.VariableName, foreachStatement.StartLocation, ExpressionContext.Default); + if (rr != null && rr.ResolvedType != null) { + BlockStatement blockStatement = foreachStatement.EmbeddedStatement as BlockStatement; + if (blockStatement == null) { + blockStatement = new BlockStatement(); + blockStatement.AddChild(foreachStatement.EmbeddedStatement); + foreachStatement.EmbeddedStatement = blockStatement; + } + + string newVariableName = foreachStatement.VariableName + "_loopVariable"; + + ExpressionStatement st = new ExpressionStatement( + new AssignmentExpression( + new IdentifierExpression(foreachStatement.VariableName), + AssignmentOperatorType.Assign, + new IdentifierExpression(newVariableName))); + blockStatement.Children.Insert(0, st); + st.Parent = blockStatement; + + foreachStatement.VariableName = newVariableName; + foreachStatement.TypeReference = ConvertType(rr.ResolvedType); + } + } + return null; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ParameterListComparer.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ParameterListComparer.cs new file mode 100644 index 000000000..c4e2b15b5 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ParameterListComparer.cs @@ -0,0 +1,56 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class ParameterListComparer : IEqualityComparer + { + public bool Equals(IMethod x, IMethod y) + { + if (GetHashCode(x) != GetHashCode(y)) + return false; + var paramsX = x.Parameters; + var paramsY = y.Parameters; + if (paramsX.Count != paramsY.Count) + return false; + if (x.TypeParameters.Count != y.TypeParameters.Count) + return false; + for (int i = 0; i < paramsX.Count; i++) { + IParameter px = paramsX[i]; + IParameter py = paramsY[i]; + if ((px.IsOut || px.IsRef) != (py.IsOut || py.IsRef)) + return false; + if (!object.Equals(px.ReturnType, py.ReturnType)) + return false; + } + return true; + } + + Dictionary cachedHashes = new Dictionary(); + + public int GetHashCode(IMethod obj) + { + int hashCode; + if (cachedHashes.TryGetValue(obj, out hashCode)) + return hashCode; + hashCode = obj.TypeParameters.Count; + unchecked { + foreach (IParameter p in obj.Parameters) { + hashCode *= 1000000579; + if (p.IsOut || p.IsRef) + hashCode += 1; + if (p.ReturnType != null) { + hashCode += p.ReturnType.GetHashCode(); + } + } + } + cachedHashes[obj] = hashCode; + return hashCode; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs new file mode 100644 index 000000000..1829fefc2 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs @@ -0,0 +1,1082 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Threading; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class DefaultProjectContent : IProjectContent + { + readonly List referencedContents = new List(); + + // we use a list of Dictionaries because we need multiple dictionaries: + // each uses another StringComparer + // (C#: StringComparer.InvariantCulture, VB: StringComparer.InvariantCultureCaseInsensitive) + // new dictionaries are added to the list when required + List> classLists = new List>(); + List> namespaces = new List>(); + + XmlDoc xmlDoc = new XmlDoc(); + IUsing defaultImports; + bool isDisposed; + + public IUsing DefaultImports { + get { + return defaultImports; + } + set { + defaultImports = value; + } + } + + public virtual object Project { + get { + return null; + } + } + + /// + /// Gets if the project content is representing the current version of the assembly. + /// This property always returns true for ParseProjectContents but might return false + /// for ReflectionProjectContent/CecilProjectContent if the file was changed. + /// + public virtual bool IsUpToDate { + get { + return true; + } + } + + public List> ClassLists { + get { + if (classLists.Count == 0) { + classLists.Add(new Dictionary(language.NameComparer)); + } + return classLists; + } + } + + public ICollection NamespaceNames { + get { + return Namespaces[0].Keys; + } + } + + protected List> Namespaces { + get { + if (namespaces.Count == 0) { + namespaces.Add(new Dictionary(language.NameComparer)); + } + return namespaces; + } + } + + // NamespaceStruct behaves like a reference type because it only consists of readonly references! + protected struct NamespaceStruct + { + public readonly List Classes; + public readonly List SubNamespaces; + + public NamespaceStruct(string name) // struct must have a parameter + { + this.Classes = new List(); + this.SubNamespaces = new List(); + } + + public NamespaceStruct MergeWith(NamespaceStruct other) + { + NamespaceStruct newStruct = new NamespaceStruct(null); + newStruct.Classes.AddRange(this.Classes); + newStruct.Classes.AddRange(other.Classes); + newStruct.SubNamespaces.AddRange(this.SubNamespaces); + newStruct.SubNamespaces.AddRange(other.SubNamespaces); + return newStruct; + } + } + + /// + /// Gets the class dictionary that uses the name comparison rules of . + /// + protected Dictionary GetClasses(LanguageProperties language) + { + for (int i = 0; i < classLists.Count; ++i) { + if (classLists[i].Comparer == language.NameComparer) + return classLists[i]; + } + Dictionary d; + if (classLists.Count > 0) { + Dictionary oldList = classLists[0]; + d = new Dictionary(oldList.Count, language.NameComparer); + foreach (KeyValuePair pair in oldList) { + // don't use d.Add(), the new name language might treat two names as equal + // that were unequal in the old dictionary + d[pair.Key] = pair.Value; + } + } else { + d = new Dictionary(language.NameComparer); + } + classLists.Add(d); + return d; + } + + /// + /// Gets the namespace dictionary that uses the name comparison rules of . + /// + protected Dictionary GetNamespaces(LanguageProperties language) + { + for (int i = 0; i < namespaces.Count; ++i) { + if (namespaces[i].Comparer == language.NameComparer) + return namespaces[i]; + } + Dictionary d; + if (namespaces.Count > 0) { + Dictionary oldList = namespaces[0]; + d = new Dictionary(oldList.Count, language.NameComparer); + foreach (KeyValuePair pair in oldList) { + NamespaceStruct ns; + if (d.TryGetValue(pair.Key, out ns)) { + // we got a name conflict due to the new NameComparer. + // This happens if a C# assembly contains the namespace "a" and "A", + // and now we're trying to get a dictionary for use in VB. + d[pair.Key] = ns.MergeWith(pair.Value); + } else { + d.Add(pair.Key, pair.Value); + } + } + } else { + d = new Dictionary(language.NameComparer); + } + namespaces.Add(d); + return d; + } + + public XmlDoc XmlDoc { + get { + return xmlDoc; + } + protected set { + xmlDoc = value; + } + } + + public ICollection Classes { + get { + lock (namespaces) { + List list = new List(ClassLists[0].Count + 10); + foreach (IClass c in ClassLists[0].Values) { + if (c is GenericClassContainer) { + GenericClassContainer gcc = (GenericClassContainer)c; + list.AddRange(gcc.RealClasses); + } else { + list.Add(c); + } + } + return list; + } + } + } + + SystemTypes systemTypes; + + /// + /// Gets a class that allows to conveniently access commonly used types in the system + /// namespace. + /// + public virtual SystemTypes SystemTypes { + get { + if (systemTypes == null) { + systemTypes = new SystemTypes(this); + } + return systemTypes; + } + } + + public ICollection ReferencedContents { + get { + return referencedContents; + } + } + + LanguageProperties language = LanguageProperties.CSharp; + + /// + /// Gets/Sets the properties of the language this project content was written in. + /// + public LanguageProperties Language { + [DebuggerStepThrough] + get { + return language; + } + set { + if (value == null) throw new ArgumentNullException(); + language = value; + } + } + + public string GetXmlDocumentation(string memberTag) + { + CheckNotDisposed(); + string desc = xmlDoc.GetDocumentation(memberTag); + if (desc != null) { + return desc; + } + lock (referencedContents) { + foreach (IProjectContent referencedContent in referencedContents) { + desc = referencedContent.XmlDoc.GetDocumentation(memberTag); + if (desc != null) { + return desc; + } + } + } + return null; + } + + public virtual void Dispose() + { + xmlDoc.Dispose(); + isDisposed = true; + } + + [Conditional("DEBUG")] + void CheckNotDisposed() + { + // TODO: this is broken - we are accessing project contents even after + // they have been unloaded, e.g. on other threads + if (!isDisposed) { + // throw new ObjectDisposedException(); + } + } + + public void AddClassToNamespaceList(IClass addClass) + { + lock (namespaces) { + AddClassToNamespaceListInternal(addClass); + } + DomCache.Clear(); + } + + /// + /// Container class that is used when multiple classes with different type parameter + /// count have the same class name. + /// + /// The GenericClassContainer is only used internally to hold the class list, it is never returned by any public API. + /// + private sealed class GenericClassContainer : DefaultClass + { + public GenericClassContainer(string fullyQualifiedName) : base(DefaultCompilationUnit.DummyCompilationUnit, fullyQualifiedName) {} + + IClass[] realClasses = new IClass[4]; + + public IEnumerable RealClasses { + get { + foreach (IClass c in realClasses) { + if (c != null) yield return c; + } + } + } + + public int RealClassCount { + get { + int count = 0; + foreach (IClass c in realClasses) { + if (c != null) count += 1; + } + return count; + } + } + + public IClass Get(int typeParameterCount) + { + if (realClasses.Length > typeParameterCount) + return realClasses[typeParameterCount]; + else + return null; + } + + public IClass GetBest(int typeParameterCount) + { + IClass c; + for (int i = typeParameterCount; i < realClasses.Length; i++) { + c = Get(i); + if (c != null) return c; + } + for (int i = typeParameterCount - 1; i >= 0; i--) { + c = Get(i); + if (c != null) return c; + } + return null; + } + + public void Set(IClass c) + { + int typeParameterCount = c.TypeParameters.Count; + if (realClasses.Length <= typeParameterCount) { + IClass[] newArray = new IClass[typeParameterCount + 2]; + realClasses.CopyTo(newArray, 0); + realClasses = newArray; + } + realClasses[typeParameterCount] = c; + } + + public void Remove(int typeParameterCount) + { + if (realClasses.Length > typeParameterCount) + realClasses[typeParameterCount] = null; + } + } + + protected void AddClassToNamespaceListInternal(IClass addClass) + { + // Freeze the class when adding it to the project content + addClass.Freeze(); + + Debug.Assert(!(addClass is CompoundClass)); + Debug.Assert(!addClass.HasCompoundClass); + + string fullyQualifiedName = addClass.FullyQualifiedName; + IClass existingClass = GetClassInternal(fullyQualifiedName, addClass.TypeParameters.Count, language); + if (existingClass != null && existingClass.TypeParameters.Count == addClass.TypeParameters.Count) { + LoggingService.Debug("Adding existing class " + addClass.Name + " from " + Path.GetFileName(addClass.CompilationUnit.FileName)); + CompoundClass compound = existingClass as CompoundClass; + if (compound != null) { + // mark the class as partial + // (VB allows specifying the 'partial' modifier only on one part) + addClass.HasCompoundClass = true; + + // add the new class to the compound class + List newParts = new List(compound.Parts); + newParts.Add(addClass); + // construct a replacement CompoundClass with the new part list + addClass = CompoundClass.Create(newParts); + LoggingService.Debug("Added new part (old part count=" + compound.Parts.Count +", new part count=" + newParts.Count + ")"); + } else { + // Instead of overwriting a class with another, treat both parts as partial. + // This fixes SD2-1217. + + if (!(addClass.IsPartial || language.ImplicitPartialClasses)) { + LoggingService.Info("Duplicate class " + fullyQualifiedName + ", creating compound"); + } else { + LoggingService.Debug("Creating compound for " + fullyQualifiedName); + } + + // Merge existing non-partial class with addClass + addClass.HasCompoundClass = true; + existingClass.HasCompoundClass = true; + + addClass = CompoundClass.Create(new[] { addClass, existingClass }); + } + } + AddClassToNamespaceListInternal2(addClass); + } + + void AddClassToNamespaceListInternal2(IClass addClass) + { + bool isReplacingExistingClass = false; + string fullyQualifiedName = addClass.FullyQualifiedName; + IClass oldDictionaryClass; + if (GetClasses(language).TryGetValue(fullyQualifiedName, out oldDictionaryClass)) { + isReplacingExistingClass = true; + GenericClassContainer gcc = oldDictionaryClass as GenericClassContainer; + if (gcc != null) { + gcc.Set(addClass); + return; + } else if (oldDictionaryClass.TypeParameters.Count != addClass.TypeParameters.Count) { + gcc = new GenericClassContainer(fullyQualifiedName); + gcc.Set(addClass); + gcc.Set(oldDictionaryClass); + addClass = gcc; + } + } + + foreach (Dictionary classes in ClassLists) { + classes[fullyQualifiedName] = addClass; + } + string nSpace = addClass.Namespace; + if (nSpace == null) { + nSpace = String.Empty; + } + CreateNamespace(nSpace); + List classList = GetNamespaces(this.language)[nSpace].Classes; + if (isReplacingExistingClass) { + for (int i = 0; i < classList.Count; i++) { + if (classList[i].FullyQualifiedName == fullyQualifiedName) { + classList[i] = addClass; + return; + } + } + } + classList.Add(addClass); + } + + void CreateNamespace(string nSpace) + { + Dictionary dict = GetNamespaces(this.language); + if (dict.ContainsKey(nSpace)) + return; + NamespaceStruct namespaceStruct = new NamespaceStruct(nSpace); + dict.Add(nSpace, namespaceStruct); + // use the same namespaceStruct for all dictionaries + foreach (Dictionary otherDict in namespaces) { + if (otherDict == dict) continue; + NamespaceStruct existingNamespaceStruct; + if (otherDict.TryGetValue(nSpace, out existingNamespaceStruct)) + otherDict[nSpace] = existingNamespaceStruct.MergeWith(namespaceStruct); + else + otherDict.Add(nSpace, namespaceStruct); + } + if (nSpace.Length == 0) + return; + // add to parent namespace + int pos = nSpace.LastIndexOf('.'); + string parent; + string subNs; + if (pos < 0) { + parent = ""; + subNs = nSpace; + } else { + parent = nSpace.Substring(0, pos); + subNs = nSpace.Substring(pos + 1); + } + CreateNamespace(parent); + dict[parent].SubNamespaces.Add(subNs); + } + + /// + /// Removes the specified namespace from all namespace lists if the namespace is empty. + /// + void RemoveEmptyNamespace(string nSpace) + { + if (nSpace == null || nSpace.Length == 0) return; + Dictionary dict = GetNamespaces(this.language); + if (!dict.ContainsKey(nSpace)) + return; + // remove only if really empty + if (dict[nSpace].Classes.Count > 0 || dict[nSpace].SubNamespaces.Count > 0) + return; + // remove the namespace from all dictionaries + foreach (Dictionary anyDict in namespaces) { + anyDict.Remove(nSpace); + } + // remove the namespace from parent's SubNamespaces list + int pos = nSpace.LastIndexOf('.'); + string parent; + string subNs; + if (pos < 0) { + parent = ""; + subNs = nSpace; + } else { + parent = nSpace.Substring(0, pos); + subNs = nSpace.Substring(pos + 1); + } + dict[parent].SubNamespaces.Remove(subNs); + RemoveEmptyNamespace(parent); // remove parent if also empty + } + + List assemblyAttributes = new List(); + + public virtual IList GetAssemblyAttributes() + { + lock (namespaces) { + return assemblyAttributes.ToArray(); + } + } + + public void RemoveCompilationUnit(ICompilationUnit unit) + { + lock (namespaces) { + foreach (IClass c in unit.Classes) { + RemoveClass(c); + } + foreach (IAttribute attr in unit.Attributes) + assemblyAttributes.Remove(attr); + } + DomCache.Clear(); + } + + public void UpdateCompilationUnit(ICompilationUnit oldUnit, ICompilationUnit parserOutput, string fileName) + { + parserOutput.Freeze(); + lock (namespaces) { + if (oldUnit != null) { + foreach (IClass c in oldUnit.Classes) + RemoveClass(c); + foreach (IAttribute attr in oldUnit.Attributes) + assemblyAttributes.Remove(attr); + } + + foreach (IClass c in parserOutput.Classes) { + AddClassToNamespaceListInternal(c); + } + assemblyAttributes.AddRange(parserOutput.Attributes); + } + DomCache.Clear(); + } + + protected void RemoveClass(IClass @class) + { + string fullyQualifiedName = @class.FullyQualifiedName; + int typeParameterCount = @class.TypeParameters.Count; + if (@class.HasCompoundClass) { + LoggingService.Debug("Removing part " + @class.CompilationUnit.FileName + " from compound class " + @class.FullyQualifiedName); + + // remove a part of a partial class + // Use "as" cast to fix SD2-680: the stored class might be a part not marked as partial + CompoundClass compound = GetClassInternal(fullyQualifiedName, typeParameterCount, language) as CompoundClass; + if (compound == null) { + LoggingService.Warn("compound class not found"); + return; + } + typeParameterCount = compound.TypeParameters.Count; + + List newParts = new List(compound.Parts); + newParts.Remove(@class); + if (newParts.Count > 1) { + LoggingService.Debug("Part removed, old part count = " + compound.Parts.Count + ", new part count=" + newParts.Count); + AddClassToNamespaceListInternal2(CompoundClass.Create(newParts)); + return; + } else if (newParts.Count == 1) { + LoggingService.Debug("Second-to-last part removed (old part count = " + compound.Parts.Count + "), overwriting compound with last part"); + newParts[0].HasCompoundClass = false; + AddClassToNamespaceListInternal2(newParts[0]); + return; + } else { // newParts.Count == 0 + // this should not be possible, the compound should have been destroyed when there was only 1 part left + LoggingService.Warn("All parts removed, remove compound"); + @class = compound; // all parts removed, remove compound class + } + } + + IClass classInDictionary; + if (!GetClasses(language).TryGetValue(fullyQualifiedName, out classInDictionary)) { + return; + } + + GenericClassContainer gcc = classInDictionary as GenericClassContainer; + if (gcc != null) { + gcc.Remove(typeParameterCount); + if (gcc.RealClassCount > 0) { + return; + } + } + + foreach (Dictionary classes in ClassLists) { + classes.Remove(fullyQualifiedName); + } + + string nSpace = @class.Namespace; + if (nSpace == null) { + nSpace = String.Empty; + } + + // Remove class from namespace lists + List classList = GetNamespaces(this.language)[nSpace].Classes; + for (int i = 0; i < classList.Count; i++) { + if (language.NameComparer.Equals(classList[i].FullyQualifiedName, fullyQualifiedName)) { + classList.RemoveAt(i); + break; + } + } + if (classList.Count == 0) { + RemoveEmptyNamespace(nSpace); + } + } + + #region Default Parser Layer dependent functions + public IClass GetClass(string typeName, int typeParameterCount) + { + return GetClass(typeName, typeParameterCount, language, GetClassOptions.Default); + } + + protected IClass GetClassInternal(string typeName, int typeParameterCount, LanguageProperties language) + { + CheckNotDisposed(); + #if DEBUG + if (System.Text.RegularExpressions.Regex.IsMatch (typeName, "`[0-9]+$")) + Debug.Assert(false, "how did a Reflection type name get here?"); + #endif + lock (namespaces) { + IClass c; + if (GetClasses(language).TryGetValue(typeName, out c)) { + GenericClassContainer gcc = c as GenericClassContainer; + if (gcc != null) { + return gcc.GetBest(typeParameterCount); + } + return c; + } + return null; + } + } + + bool IsAccessibleClass(IClass c) + { + // check the outermost class (which is either public or internal) + while (c.DeclaringType != null) + c = c.DeclaringType; + return c.IsPublic || c.ProjectContent.InternalsVisibleTo(this); + } + + public IClass GetClass(string typeName, int typeParameterCount, LanguageProperties language, GetClassOptions options) + { + IClass c = GetClassInternal(typeName, typeParameterCount, language); + if (c != null && c.TypeParameters.Count == typeParameterCount) { + return c; + } + + // Search in references: + if ((options & GetClassOptions.LookInReferences) != 0) { + lock (referencedContents) { + foreach (IProjectContent content in referencedContents) { + // Look for the class in the referenced content. + // Don't do a inner-class search in the recursive call - one search + // done by this GetClass call is sufficient. + IClass contentClass = content.GetClass( + typeName, typeParameterCount, language, + options & ~(GetClassOptions.LookInReferences | GetClassOptions.LookForInnerClass)); + if (contentClass != null) { + if (contentClass.TypeParameters.Count == typeParameterCount + && IsAccessibleClass(contentClass)) + { + return contentClass; + } else { + c = contentClass; + } + } + } + } + } + + if ((options & GetClassOptions.LookForInnerClass) != 0) { + // not found -> maybe nested type -> trying to find class that contains this one. + int lastIndex = typeName.LastIndexOf('.'); + if (lastIndex > 0) { + string outerName = typeName.Substring(0, lastIndex); + IClass upperClass = GetClass(outerName, typeParameterCount, language, options); + if (upperClass != null) { + foreach (IClass upperBaseClass in upperClass.ClassInheritanceTree) { + IList innerClasses = upperBaseClass.InnerClasses; + if (innerClasses != null) { + string innerName = typeName.Substring(lastIndex + 1); + foreach (IClass innerClass in innerClasses) { + if (language.NameComparer.Equals(innerClass.Name, innerName)) { + if (innerClass.TypeParameters.Count == typeParameterCount) { + return innerClass; + } else { + // store match + c = innerClass; + } + } + } + } + } + } + } + } + if ((options & GetClassOptions.ExactMatch) == GetClassOptions.ExactMatch) { + return null; + } else { + // no matching class found - we'll return a class with different type paramter count + return c; + } + } + + public List GetNamespaceContents(string nameSpace) + { + List namespaceList = new List(); + AddNamespaceContents(namespaceList, nameSpace, language, true); + return namespaceList; + } + + public List GetAllContents() + { + List list = new List(); + AddAllContents(list, this.language, true); + return list; + } + + /// + /// Adds the contents of all namespaces in this project to the . + /// + /// If true, contents of referenced projects will be added as well (not recursive - just 1 level deep). + public void AddAllContents(List list, LanguageProperties language, bool lookInReferences) + { + if (lookInReferences) { + lock (referencedContents) { + foreach (IProjectContent content in referencedContents) { + content.AddAllContents(list, language, false); + } + } + } + lock (namespaces) { + Dictionary dict = GetNamespaces(language); + foreach (var namespaceStruct in dict.Values) { + AddNamespaceStructContents(list, namespaceStruct, language, lookInReferences); + } + } + } + + /// + /// Adds the contents of the specified to the . + /// + /// If true, contents of referenced projects will be added as well (not recursive - just 1 level deep). + public void AddNamespaceContents(List list, string nameSpace, LanguageProperties language, bool lookInReferences) + { + if (nameSpace == null) { + return; + } + + if (lookInReferences) { + lock (referencedContents) { + foreach (IProjectContent content in referencedContents) { + content.AddNamespaceContents(list, nameSpace, language, false); + } + } + } + + lock (namespaces) { + Dictionary dict = GetNamespaces(language); + if (dict.ContainsKey(nameSpace)) { + NamespaceStruct ns = dict[nameSpace]; + AddNamespaceStructContents(list, ns, language, lookInReferences); + } + } + } + + void AddNamespaceStructContents(List list, NamespaceStruct ns, LanguageProperties language, bool lookInReferences) + { + int newCapacity = list.Count + ns.Classes.Count + ns.SubNamespaces.Count; + if (list.Capacity < newCapacity) + list.Capacity = Math.Max(list.Count * 2, newCapacity); + foreach (IClass c in ns.Classes) { + if (c is GenericClassContainer) { + foreach (IClass realClass in ((GenericClassContainer)c).RealClasses) { + AddNamespaceContentsClass(list, realClass, language, lookInReferences); + } + } else { + AddNamespaceContentsClass(list, c, language, lookInReferences); + } + } + foreach (string subns in ns.SubNamespaces) { + NamespaceEntry subnsEntry = new NamespaceEntry(subns); + if (!list.Contains(subnsEntry)) // PERF + list.Add(subnsEntry); + } + } + + void AddNamespaceContentsClass(List list, IClass c, LanguageProperties language, bool lookInReferences) + { + if (c.IsInternal && !lookInReferences) { + // internal class and we are looking at it from another project content + return; + } + if (language.ShowInNamespaceCompletion(c)) + list.Add(c); + if (language.ImportModules && c.ClassType == ClassType.Module) { + foreach (IMember m in c.Methods) { + if (m.IsAccessible(null, false)) + list.Add(m); + } + foreach (IMember m in c.Events) { + if (m.IsAccessible(null, false)) + list.Add(m); + } + foreach (IMember m in c.Fields) { + if (m.IsAccessible(null, false)) + list.Add(m); + } + foreach (IMember m in c.Properties) { + if (m.IsAccessible(null, false)) + list.Add(m); + } + } + } + + public bool NamespaceExists(string name) + { + return NamespaceExists(name, language, true); + } + + public bool NamespaceExists(string name, LanguageProperties language, bool lookInReferences) + { + if (name == null) { + return false; + } + + if (lookInReferences) { + lock (referencedContents) { + foreach (IProjectContent content in referencedContents) { + if (content.NamespaceExists(name, language, false)) { + return true; + } + } + } + } + + lock (namespaces) { + return GetNamespaces(language).ContainsKey(name); + } + } + + bool MatchesRequest(SearchTypeRequest request, ref SearchTypeResult result) + { + if (result.NamespaceResult != null) + return request.TypeParameterCount == 0; + else { + IReturnType rt = result.Result; + if (rt == null) + return false; + if (rt.TypeArgumentCount != request.TypeParameterCount) + return false; + IClass c = rt.GetUnderlyingClass(); + if (c != null) + return IsAccessibleClass(c); + else + return true; + } + } + + public SearchTypeResult SearchType(SearchTypeRequest request) + { + string name = request.Name; + if (string.IsNullOrEmpty(name)) { + return SearchTypeResult.Empty; + } + + // 'result' holds the fall-back result if no result with the right type parameter count is found. + SearchTypeResult result = SearchTypeResult.Empty; + + if (name.IndexOf('.') < 0) { + for (IClass outerClass = request.CurrentType; outerClass != null; outerClass = outerClass.DeclaringType) { + // Try inner classes (in full inheritance tree) + // Don't use loop with cur = cur.BaseType because of inheritance cycles + foreach (IClass baseClass in outerClass.ClassInheritanceTree) { + if (baseClass.ClassType == ClassType.Class || baseClass.ClassType == ClassType.Struct || baseClass.ClassType == ClassType.Module) { + foreach (IClass innerClass in baseClass.InnerClasses) { + if (language.NameComparer.Equals(innerClass.Name, name)) { + result = new SearchTypeResult(innerClass); + if (MatchesRequest(request, ref result)) { + return result; + } + } + } + } + } + } + } + + for (IUsingScope usingScope = request.CurrentUsingScope; usingScope != null; usingScope = usingScope.Parent) { + string fullname; + if (string.IsNullOrEmpty(usingScope.NamespaceName)) { + // Try if name is already the full type name + fullname = name; + } else { + fullname = usingScope.NamespaceName + "." + name; + } + IClass c = GetClass(fullname, request.TypeParameterCount); + if (c != null) { + result = new SearchTypeResult(c); + if (MatchesRequest(request, ref result)) { + return result; + } + } + if (NamespaceExists(fullname)) { + result = new SearchTypeResult(fullname, null); + if (MatchesRequest(request, ref result)) { + return result; + } + } + + // prefer aliases over imported types + foreach (IUsing u in usingScope.Usings) { + if (u.HasAliases) { + if (SearchTypeInUsing(u, request, ref result)) + return result; + } + } + foreach (IUsing u in usingScope.Usings) { + if (!u.HasAliases) { + if (SearchTypeInUsing(u, request, ref result)) + return result; + } + } + } + + if (defaultImports != null) { + if (SearchTypeInUsing(defaultImports, request, ref result)) + return result; + } + return result; + } + + bool SearchTypeInUsing(IUsing u, SearchTypeRequest request, ref SearchTypeResult result) + { + foreach (IReturnType r in u.SearchType(request.Name, request.TypeParameterCount)) { + result = new SearchTypeResult(r, u); + if (MatchesRequest(request, ref result)) { + return true; + } + } + string nsResult = u.SearchNamespace(request.Name); + if (nsResult != null) { + result = new SearchTypeResult(nsResult, null); + if (MatchesRequest(request, ref result)) { + return true; + } + } + return false; + } + + /// + /// Gets the position of a member in this project content (not a referenced one). + /// + /// The full class name in Reflection syntax (always case sensitive, ` for generics) + /// Whether to search in referenced project contents. + public IClass GetClassByReflectionName(string className, bool lookInReferences) + { + if (className == null) + throw new ArgumentNullException("className"); + int typeParameterCount; + className = ReflectionLayer.ReflectionClass.ConvertReflectionNameToFullName(className, out typeParameterCount); + GetClassOptions options = GetClassOptions.Default; + if (!lookInReferences) + options &= ~GetClassOptions.LookInReferences; + return GetClass(className, typeParameterCount, LanguageProperties.CSharp, options); + } + + /// + /// Gets the position of a member in this project content (not a referenced one). + /// + /// The member name in Reflection syntax (always case sensitive, ` for generics). + /// member name = [ExplicitInterface .] MemberName [`TypeArgumentCount] [(Parameters)] + public static IMember GetMemberByReflectionName(IClass curClass, string fullMemberName) + { + if (curClass == null) + return null; + int pos = fullMemberName.IndexOf('('); + if (pos > 0) { + // is method call + + int colonPos = fullMemberName.LastIndexOf(':'); + if (colonPos > 0) { + fullMemberName = fullMemberName.Substring(0, colonPos); + } + + string interfaceName = null; + string memberName = fullMemberName.Substring(0, pos); + int pos2 = memberName.LastIndexOf('.'); + if (pos2 > 0) { + interfaceName = memberName.Substring(0, pos2); + memberName = memberName.Substring(pos2 + 1); + } + + // put class name in front of full member name because we'll compare against it later + fullMemberName = curClass.DotNetName + "." + fullMemberName; + + IMethod firstMethod = null; + foreach (IMethod m in curClass.Methods) { + if (m.Name == memberName) { + if (firstMethod == null) firstMethod = m; + StringBuilder dotnetName = new StringBuilder(m.DotNetName); + dotnetName.Append('('); + for (int i = 0; i < m.Parameters.Count; i++) { + if (i > 0) dotnetName.Append(','); + if (m.Parameters[i].ReturnType != null) { + dotnetName.Append(m.Parameters[i].ReturnType.DotNetName); + } + } + dotnetName.Append(')'); + if (dotnetName.ToString() == fullMemberName) { + return m; + } + } + } + return firstMethod; + } else { + string interfaceName = null; + string memberName = fullMemberName; + pos = memberName.LastIndexOf('.'); + if (pos > 0) { + interfaceName = memberName.Substring(0, pos); + memberName = memberName.Substring(pos + 1); + } + // get first method with that name, but prefer method without parameters + IMethod firstMethod = null; + foreach (IMethod m in curClass.Methods) { + if (m.Name == memberName) { + if (firstMethod == null || m.Parameters.Count == 0) + firstMethod = m; + } + } + if (firstMethod != null) + return firstMethod; + return curClass.SearchMember(memberName, LanguageProperties.CSharp); + } + } + + public FilePosition GetPosition(IEntity d) + { + IMember m = d as IMember; + IClass c = d as IClass; + if (m != null) { + return new FilePosition(m.DeclaringType.CompilationUnit, m.Region.BeginLine, m.Region.BeginColumn); + } else if (c != null) { + return new FilePosition(c.CompilationUnit, c.Region.BeginLine, c.Region.BeginColumn); + } else { + return FilePosition.Empty; + } + } + #endregion + + public void AddReferencedContent(IProjectContent pc) + { + if (pc != null) { + lock (this.ReferencedContents) { + this.ReferencedContents.Add(pc); + } + } + } + + public event EventHandler ReferencedContentsChanged; + + protected virtual void OnReferencedContentsChanged(EventArgs e) + { + systemTypes = null; // re-create system types + DomCache.Clear(); + if (ReferencedContentsChanged != null) { + ReferencedContentsChanged(this, e); + } + } + + public bool InternalsVisibleTo(IProjectContent otherProjectContent) + { + return this == otherProjectContent; + } + + public static readonly IProjectContent DummyProjectContent = new DummyContent(); + + private class DummyContent : DefaultProjectContent + { + public override string ToString() + { + return "[DummyProjectContent]"; + } + + public override SystemTypes SystemTypes { + get { + return HostCallback.GetCurrentProjectContent().SystemTypes; + } + } + } + + /// + public virtual string AssemblyName { + get { + return null; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DomAssemblyName.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DomAssemblyName.cs new file mode 100644 index 000000000..5d64ec76d --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DomAssemblyName.cs @@ -0,0 +1,93 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Similar to System.Reflection.AssemblyName, but does not raise an exception + /// on invalid assembly names. (See SD2-1307) + /// + public sealed class DomAssemblyName : IEquatable + { + readonly string fullAssemblyName; + readonly string shortName, culture, publicKeyToken; + readonly Version version; + + public DomAssemblyName(string fullAssemblyName) + { + this.fullAssemblyName = fullAssemblyName; + string[] components = fullAssemblyName.Split(','); + shortName = components[0]; + for (int i = 1; i < components.Length; i++) { + string val = components[i].Trim(); + int pos = val.IndexOf('='); + if (pos > 0) { + switch (val.Substring(0, pos)) { + case "Version": + string versionText = val.Substring(pos + 1); + Version.TryParse(versionText, out version); + break; + case "Culture": + culture = val.Substring(pos + 1); + break; + case "PublicKeyToken": + publicKeyToken = val.Substring(pos + 1); + break; + } + } + } + } + + public string FullName { + get { return fullAssemblyName; } + } + + public string ShortName { + get { return shortName; } + } + + public Version Version { + get { return version; } + } + + public string Culture { + get { return culture; } + } + + public string PublicKeyToken { + get { return publicKeyToken; } + } + + public override string ToString() + { + return fullAssemblyName; + } + + public override int GetHashCode() + { + return fullAssemblyName.GetHashCode(); + } + + public override bool Equals(object obj) + { + return Equals(obj as DomAssemblyName); + } + + public bool Equals(DomAssemblyName other) + { + return other != null && fullAssemblyName == other.fullAssemblyName; + } + + internal static DomAssemblyName[] Convert(System.Reflection.AssemblyName[] names) + { + if (names == null) return null; + DomAssemblyName[] n = new DomAssemblyName[names.Length]; + for (int i = 0; i < names.Length; i++) { + n[i] = new DomAssemblyName(names[i].FullName); + } + return n; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/IProjectContent.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/IProjectContent.cs new file mode 100644 index 000000000..9ed94fa8c --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/IProjectContent.cs @@ -0,0 +1,287 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public interface IProjectContent + { + void Dispose(); + + XmlDoc XmlDoc { + get; + } + + /// + /// Gets if the project content is representing the current version of the assembly. + /// This property always returns true for ParseProjectContents but might return false + /// for ReflectionProjectContent/CecilProjectContent if the file was changed. + /// + bool IsUpToDate { + get; + } + + ICollection Classes { + get; + } + + /// + /// Gets the list of namespaces defined in this project content. Does not include namespaces from + /// referenced project contents. + /// + ICollection NamespaceNames { + get; + } + + /// + /// Gets the list of referenced project contents. + /// + ICollection ReferencedContents { + get; + } + + event EventHandler ReferencedContentsChanged; + + /// + /// Gets the properties of the language this project content was written in. + /// + LanguageProperties Language { + get; + } + + /// + /// Gets the default imports of the project content. Can return null. + /// + IUsing DefaultImports { + get; + } + + /// + /// Gets the project for this project content. Returns null for reflection project contents. + /// The type used for project objects depends on the host application. + /// + object Project { + get; + } + + /// + /// Gets a class that allows to conveniently access commonly used types in the system + /// namespace. + /// + SystemTypes SystemTypes { + get; + } + + IList GetAssemblyAttributes(); + + string GetXmlDocumentation(string memberTag); + + void AddClassToNamespaceList(IClass addClass); + void RemoveCompilationUnit(ICompilationUnit oldUnit); + void UpdateCompilationUnit(ICompilationUnit oldUnit, ICompilationUnit parserOutput, string fileName); + + IClass GetClass(string typeName, int typeParameterCount); + bool NamespaceExists(string name); + List GetNamespaceContents(string nameSpace); + List GetAllContents(); + + IClass GetClass(string typeName, int typeParameterCount, LanguageProperties language, GetClassOptions options); + bool NamespaceExists(string name, LanguageProperties language, bool lookInReferences); + /// + /// Adds the contents of the specified to the . + /// + /// If true, contents of referenced projects will be added as well (not recursive - just 1 level deep). + void AddNamespaceContents(List list, string subNameSpace, LanguageProperties language, bool lookInReferences); + /// + /// Adds the contents of all namespaces in this project to the . + /// + /// If true, contents of referenced projects will be added as well (not recursive - just 1 level deep). + void AddAllContents(List list, LanguageProperties language, bool lookInReferences); + + SearchTypeResult SearchType(SearchTypeRequest request); + + /// + /// Gets the position of a member in this project content (not a referenced one). + /// + /// The full class name in Reflection syntax (always case sensitive, ` for generics) + /// Whether to search in referenced project contents. + IClass GetClassByReflectionName(string fullMemberName, bool lookInReferences); + + /// + /// Gets the definition position of the class/member. + /// + /// The entity to get the position from. + FilePosition GetPosition(IEntity entity); + + /// + /// Gets whether internals in the project content are visible to the other project content. + /// + bool InternalsVisibleTo(IProjectContent otherProjectContent); + + /// + /// Gets the name of the assembly. + /// + string AssemblyName { + get; + } + } + + [Flags] + public enum GetClassOptions + { + None = 0, + /// + /// Also look in referenced project contents. + /// + LookInReferences = 1, + /// + /// Try if the class is an inner class. + /// + LookForInnerClass = 2, + /// + /// Do not return a class with the wrong type parameter count. + /// If this flag is not set, GetClass will return a class with the same name but a different + /// type parameter count if no exact match is found. + /// + ExactMatch = 4, + /// + /// Default = LookInReferences + LookForInnerClass + /// + Default = LookInReferences | LookForInnerClass + } + + public sealed class SearchTypeRequest + { + IUsingScope currentUsingScope; + ICompilationUnit currentCompilationUnit; + + public string Name { get; set; } + public int TypeParameterCount { get; set; } + public IClass CurrentType { get; set; } + public int CaretLine { get; set; } + public int CaretColumn { get; set; } + + public ICompilationUnit CurrentCompilationUnit { + get { return currentCompilationUnit; } + set { + if (value == null) + throw new ArgumentNullException("CurrentCompilationUnit"); + currentCompilationUnit = value; + } + } + + public IUsingScope CurrentUsingScope { + get { return currentUsingScope; } + set { + if (value == null) + throw new ArgumentNullException("CurrentUsingScope"); + currentUsingScope = value; + } + } + + public SearchTypeRequest(string name, int typeParameterCount, IClass currentType, int caretLine, int caretColumn) + { + if (currentType == null) + throw new ArgumentNullException("currentType"); + this.Name = name; + this.TypeParameterCount = typeParameterCount; + this.CurrentCompilationUnit = currentType.CompilationUnit; + this.CurrentType = currentType != null ? currentType.GetCompoundClass() : null; + this.CaretLine = caretLine; + this.CaretColumn = caretColumn; + this.CurrentUsingScope = currentType.UsingScope; + } + + public SearchTypeRequest(string name, int typeParameterCount, IClass currentType, ICompilationUnit currentCompilationUnit, int caretLine, int caretColumn) + { + if (currentCompilationUnit == null) + throw new ArgumentNullException("currentCompilationUnit"); + this.Name = name; + this.TypeParameterCount = typeParameterCount; + this.CurrentCompilationUnit = currentCompilationUnit; + this.CurrentType = currentType != null ? currentType.GetCompoundClass() : null; + this.CaretLine = caretLine; + this.CaretColumn = caretColumn; + this.CurrentUsingScope = (currentType != null) ? currentType.UsingScope : currentCompilationUnit.UsingScope; + } + } + + public struct SearchTypeResult + { + public static readonly SearchTypeResult Empty = default(SearchTypeResult); + + readonly IReturnType result; + readonly IUsing usedUsing; + readonly string namespaceResult; + + public SearchTypeResult(IReturnType result) : this(result, null) {} + + public SearchTypeResult(IClass c) : this(c != null ? c.DefaultReturnType : null) {} + + public SearchTypeResult(IReturnType result, IUsing usedUsing) + { + this.result = result; + this.usedUsing = usedUsing; + this.namespaceResult = null; + } + + public SearchTypeResult(string namespaceResult, IUsing usedUsing) + { + this.result = null; + this.usedUsing = usedUsing; + this.namespaceResult = namespaceResult; + } + + /// + /// Gets the result type. + /// + public IReturnType Result { + get { return result; } + } + + /// + /// Gets the using that was used for this type lookup. + /// + public IUsing UsedUsing { + get { return usedUsing; } + } + + public string NamespaceResult { + get { return namespaceResult; } + } + } + + /// + /// Used in 'GetNamespaceContents' result to represent a namespace. + /// + public class NamespaceEntry : ICompletionEntry + { + public string Name { get; private set; } + + public NamespaceEntry(string name) + { + if (name == null) + throw new ArgumentNullException("name"); + this.Name = name; + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override bool Equals(object obj) + { + NamespaceEntry e = obj as NamespaceEntry; + return e != null && e.Name == this.Name; + } + + public override string ToString() + { + return Name; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ParseInformation.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ParseInformation.cs new file mode 100644 index 000000000..27e10b3bd --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ParseInformation.cs @@ -0,0 +1,62 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Holds the parse information for a file. + /// This class is immutable and thread-safe. + /// + public class ParseInformation : Immutable + { + ICompilationUnit unit; + + /// + /// Gets the compilation unit. + /// This property never returns null. + /// + public ICompilationUnit CompilationUnit { + get { return unit; } + } + + /// + /// Gets the last compilation unit that was valid (=no parse errors). + /// This property might be null. + /// + [ObsoleteAttribute] + public ICompilationUnit ValidCompilationUnit { get { return unit; } } + + /// + /// Gets the last compilation unit that was invalid (=had parse errors). + /// This property is null if the most recent compilation unit is valid. + /// + [ObsoleteAttribute] + public ICompilationUnit DirtyCompilationUnit { get { return unit; } } + + /// + /// Gets the best compilation unit. + /// This returns the ValidCompilationUnit if one exists, otherwise + /// the DirtyCompilationUnit. + /// + [ObsoleteAttribute] + public ICompilationUnit BestCompilationUnit { get { return unit; } } + + /// + /// Gets the most recent compilation unit. The unit might be valid or invalid. + /// + [ObsoleteAttribute] + public ICompilationUnit MostRecentCompilationUnit { get { return unit; } } + + public ParseInformation(ICompilationUnit unit) + { + if (unit == null) + throw new ArgumentNullException("unit"); + unit.Freeze(); +// if (!unit.IsFrozen) +// throw new ArgumentException("unit must be frozen for use in ParseInformation"); + this.unit = unit; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ProjectContentRegistry.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ProjectContentRegistry.cs new file mode 100644 index 000000000..78942dace --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ProjectContentRegistry.cs @@ -0,0 +1,338 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Xml; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Contains project contents read from external assemblies. + /// Caches loaded assemblies in memory and optionally also to disk. + /// + public class ProjectContentRegistry : IDisposable + { + internal DomPersistence persistence; + Dictionary contents = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Disposes all project contents stored in this registry. + /// + public virtual void Dispose() + { + List list; + lock (contents) { + list = new List(contents.Values); + contents.Clear(); + } + // dispose outside the lock + foreach (IProjectContent pc in list) { + pc.Dispose(); + } + } + + /// + /// Activate caching assemblies to disk. + /// Cache files will be saved in the specified directory. + /// + public DomPersistence ActivatePersistence(string cacheDirectory) + { + if (cacheDirectory == null) { + throw new ArgumentNullException("cacheDirectory"); + } else if (persistence != null && cacheDirectory == persistence.CacheDirectory) { + return persistence; + } else { + persistence = new DomPersistence(cacheDirectory, this); + return persistence; + } + } + + + + ReflectionProjectContent mscorlibContent; + + /// + /// Runs the method inside the lock of the registry. + /// Use this method if you want to call multiple methods on the ProjectContentRegistry and ensure + /// that no other thread accesses the registry while your method runs. + /// + public void RunLocked(ThreadStart method) + { + lock (contents) { + method(); + } + } + + public virtual IProjectContent Mscorlib { + get { + if (mscorlibContent != null) return mscorlibContent; + lock (contents) { + if (contents.ContainsKey("mscorlib")) { + mscorlibContent = (ReflectionProjectContent)contents["mscorlib"]; + return contents["mscorlib"]; + } + int time = LoggingService.IsDebugEnabled ? Environment.TickCount : 0; + LoggingService.Debug("Loading PC for mscorlib..."); + if (persistence != null) { + mscorlibContent = persistence.LoadProjectContentByAssemblyName(MscorlibAssembly.FullName); + if (mscorlibContent != null) { + if (time != 0) { + LoggingService.Debug("Loaded mscorlib from cache in " + (Environment.TickCount - time) + " ms"); + } + } + } + if (mscorlibContent == null) { + // We're using Cecil now for everything to find bugs in CecilReader faster + //mscorlibContent = CecilReader.LoadAssembly(MscorlibAssembly.Location, this); + + // After SD 2.1 Beta 2, we're back to Reflection + mscorlibContent = new ReflectionProjectContent(MscorlibAssembly, this); + if (time != 0) { + //LoggingService.Debug("Loaded mscorlib with Cecil in " + (Environment.TickCount - time) + " ms"); + LoggingService.Debug("Loaded mscorlib with Reflection in " + (Environment.TickCount - time) + " ms"); + } + if (persistence != null) { + persistence.SaveProjectContent(mscorlibContent); + LoggingService.Debug("Saved mscorlib to cache"); + } + } + contents["mscorlib"] = mscorlibContent; + contents[mscorlibContent.AssemblyFullName] = mscorlibContent; + contents[mscorlibContent.AssemblyLocation] = mscorlibContent; + return mscorlibContent; + } + } + } + + public virtual ICollection GetLoadedProjectContents() + { + lock (contents) { // we need to return a copy because we have to lock + return new List(contents.Values); + } + } + + /// + /// Unloads the specified project content, causing it to be reloaded when + /// GetProjectContentForReference is called the next time. + /// Warning: do not unload project contents that are still in use! Doing so will result + /// in an ObjectDisposedException when the unloaded project content is used the next time! + /// + public void UnloadProjectContent(IProjectContent pc) + { + if (pc == null) + throw new ArgumentNullException("pc"); + LoggingService.Debug("ProjectContentRegistry.UnloadProjectContent: " + pc); + lock (contents) { + // find all keys used for the project content - might be the short name/full name/file name + List keys = new List(); + foreach (KeyValuePair pair in contents) { + if (pair.Value == pc) keys.Add(pair.Key); + } + foreach (string key in keys) { + contents.Remove(key); + } + } + pc.Dispose(); + } + + public IProjectContent GetExistingProjectContent(DomAssemblyName assembly) + { + return GetExistingProjectContent(assembly.FullName); + } + + public virtual IProjectContent GetExistingProjectContent(string fileNameOrAssemblyName) + { + lock (contents) { + if (contents.ContainsKey(fileNameOrAssemblyName)) { + return contents[fileNameOrAssemblyName]; + } + } + + // GetProjectContentForReference supports redirecting .NET base assemblies to the correct version, + // so GetExistingProjectContent must support it, too (otherwise assembly interdependencies fail + // to resolve correctly when a .NET 1.0 assembly is used in a .NET 2.0 project) + int pos = fileNameOrAssemblyName.IndexOf(','); + if (pos > 0) { + string shortName = fileNameOrAssemblyName.Substring(0, pos); + Assembly assembly = GetDefaultAssembly(shortName); + if (assembly != null) { + lock (contents) { + if (contents.ContainsKey(assembly.FullName)) { + return contents[assembly.FullName]; + } + } + } + } + + return null; + } + + public virtual IProjectContent GetProjectContentForReference(string itemInclude, string itemFileName) + { + lock (contents) { + IProjectContent pc = GetExistingProjectContent(itemFileName); + if (pc != null) { + return pc; + } + + LoggingService.Debug("Loading PC for " + itemInclude); + + string shortName = itemInclude; + int pos = shortName.IndexOf(','); + if (pos > 0) + shortName = shortName.Substring(0, pos); + + #if DEBUG + int time = Environment.TickCount; + #endif + + try { + pc = LoadProjectContent(itemInclude, itemFileName); + } catch (BadImageFormatException ex) { + HostCallback.ShowAssemblyLoadErrorInternal(itemFileName, itemInclude, ex.Message); + } catch (Exception ex) { + HostCallback.ShowError("Error loading assembly " + itemFileName, ex); + } finally { + #if DEBUG + LoggingService.Debug(string.Format("Loaded {0} in {1}ms", itemInclude, Environment.TickCount - time)); + #endif + } + + if (pc != null) { + ReflectionProjectContent reflectionProjectContent = pc as ReflectionProjectContent; + if (reflectionProjectContent != null) { + reflectionProjectContent.InitializeReferences(); + if (reflectionProjectContent.AssemblyFullName != null) { + contents[reflectionProjectContent.AssemblyFullName] = pc; + } + } + contents[itemInclude] = pc; + contents[itemFileName] = pc; + } + return pc; + } + } + + protected virtual IProjectContent LoadProjectContent(string itemInclude, string itemFileName) + { + string shortName = itemInclude; + int pos = shortName.IndexOf(','); + if (pos > 0) + shortName = shortName.Substring(0, pos); + + Assembly assembly = GetDefaultAssembly(shortName); + ReflectionProjectContent pc = null; + if (assembly != null) { + if (persistence != null) { + pc = persistence.LoadProjectContentByAssemblyName(assembly.FullName); + } + if (pc == null) { + pc = new ReflectionProjectContent(assembly, this); + if (persistence != null) { + persistence.SaveProjectContent(pc); + } + } + } else { + // find real file name for cecil: + if (File.Exists(itemFileName)) { + if (persistence != null) { + pc = persistence.LoadProjectContentByAssemblyName(itemFileName); + } + if (pc == null) { + pc = CecilReader.LoadAssembly(itemFileName, this); + + if (persistence != null) { + persistence.SaveProjectContent(pc); + } + } + } else { + DomAssemblyName asmName = GacInterop.FindBestMatchingAssemblyName(itemInclude); + if (persistence != null && asmName != null) { + //LoggingService.Debug("Looking up in DOM cache: " + asmName.FullName); + pc = persistence.LoadProjectContentByAssemblyName(asmName.FullName); + } + if (pc == null && asmName != null) { + string subPath = Path.Combine(asmName.ShortName, GetVersion__Token(asmName)); + subPath = Path.Combine(subPath, asmName.ShortName + ".dll"); + foreach (string dir in Directory.GetDirectories(GacInterop.GacRootPathV4, "GAC*")) { + itemFileName = Path.Combine(dir, subPath); + if (File.Exists(itemFileName)) { + pc = CecilReader.LoadAssembly(itemFileName, this); + if (persistence != null) { + persistence.SaveProjectContent(pc); + } + break; + } + } + } + if (pc == null) { + HostCallback.ShowAssemblyLoadErrorInternal(itemFileName, itemInclude, "Could not find assembly file."); + } + } + } + return pc; + } + + static string GetVersion__Token(DomAssemblyName asmName) + { + StringBuilder b = new StringBuilder(asmName.Version.ToString()); + b.Append("__"); + b.Append(asmName.PublicKeyToken); + return b.ToString(); + } + + public static Assembly MscorlibAssembly { + get { + return typeof(object).Assembly; + } + } + + public static Assembly SystemAssembly { + get { + return typeof(Uri).Assembly; + } + } + + protected virtual Assembly GetDefaultAssembly(string shortName) + { + // These assemblies are already loaded by SharpDevelop, so we + // don't need to load them in a separate AppDomain/with Cecil. + switch (shortName) { + case "mscorlib": + return MscorlibAssembly; + case "System": // System != mscorlib !!! + return SystemAssembly; + case "System.Core": + return typeof(System.Linq.Enumerable).Assembly; + case "System.Xml": + case "System.XML": + return typeof(XmlReader).Assembly; + case "System.Data": + case "System.Windows.Forms": + case "System.Runtime.Remoting": + return Assembly.Load(shortName + ", Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); + case "System.Configuration": + case "System.Design": + case "System.Deployment": + case "System.Drawing": + case "System.Drawing.Design": + case "System.ServiceProcess": + case "System.Security": + case "System.Management": + case "System.Messaging": + case "System.Web": + case "System.Web.Services": + return Assembly.Load(shortName + ", Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); + case "Microsoft.VisualBasic": + return Assembly.Load("Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); + default: + return null; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ReflectionProjectContent.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ReflectionProjectContent.cs new file mode 100644 index 000000000..76cf2710a --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ReflectionProjectContent.cs @@ -0,0 +1,201 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +using ICSharpCode.SharpDevelop.Dom.ReflectionLayer; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class ReflectionProjectContent : DefaultProjectContent + { + string assemblyFullName; + string assemblyName; + DomAssemblyName[] referencedAssemblyNames; + ICompilationUnit assemblyCompilationUnit; + string assemblyLocation; + ProjectContentRegistry registry; + + public string AssemblyLocation { + get { + return assemblyLocation; + } + } + + public string AssemblyFullName { + get { + return assemblyFullName; + } + } + + public override string AssemblyName { + get { return assemblyName; } + } + + /// + /// Gets the list of assembly names referenced by this project content. + /// + public IList ReferencedAssemblyNames { + get { return Array.AsReadOnly(referencedAssemblyNames); } + } + + public ICompilationUnit AssemblyCompilationUnit { + get { return assemblyCompilationUnit; } + } + + public override IList GetAssemblyAttributes() + { + return assemblyCompilationUnit.Attributes; + } + + DateTime assemblyFileLastWriteTime; + + /// + /// Gets if the project content is representing the current version of the assembly. + /// This property always returns true for ParseProjectContents but might return false + /// for ReflectionProjectContent/CecilProjectContent if the file was changed. + /// + public override bool IsUpToDate { + get { + DateTime newWriteTime; + try { + newWriteTime = File.GetLastWriteTimeUtc(assemblyLocation); + } catch (Exception ex) { + LoggingService.Warn(ex); + return true; + } + return assemblyFileLastWriteTime == newWriteTime; + } + } + + public ReflectionProjectContent(Assembly assembly, ProjectContentRegistry registry) + : this(assembly, assembly.Location, registry) + { + } + + public ReflectionProjectContent(Assembly assembly, string assemblyLocation, ProjectContentRegistry registry) + : this(assembly.FullName, assemblyLocation, DomAssemblyName.Convert(assembly.GetReferencedAssemblies()), registry) + { + foreach (Type type in assembly.GetExportedTypes()) { + string name = type.FullName; + if (name.IndexOf('+') < 0) { // type.IsNested + AddClassToNamespaceListInternal(new ReflectionClass(assemblyCompilationUnit, type, name, null)); + } + } + InitializeSpecialClasses(); + AddAssemblyAttributes(assembly); + assemblyCompilationUnit.Freeze(); + } + + /// + /// Adds assembly attributes from the specified assembly. + /// + /// The constructor already does this, this method is meant for unit tests only! + /// + public void AddAssemblyAttributes(Assembly assembly) + { + ReflectionClass.AddAttributes(this, assemblyCompilationUnit.Attributes, CustomAttributeData.GetCustomAttributes(assembly)); + } + + public ReflectionProjectContent(string assemblyFullName, string assemblyLocation, DomAssemblyName[] referencedAssemblies, ProjectContentRegistry registry) + { + if (assemblyFullName == null) + throw new ArgumentNullException("assemblyFullName"); + if (assemblyLocation == null) + throw new ArgumentNullException("assemblyLocation"); + if (registry == null) + throw new ArgumentNullException("registry"); + + this.registry = registry; + this.assemblyFullName = assemblyFullName; + this.assemblyName = (assemblyFullName.IndexOf(',') > -1) ? assemblyFullName.Substring(0, assemblyFullName.IndexOf(',')) : assemblyFullName; + this.referencedAssemblyNames = referencedAssemblies; + this.assemblyLocation = assemblyLocation; + this.assemblyCompilationUnit = new DefaultCompilationUnit(this); + + try { + assemblyFileLastWriteTime = File.GetLastWriteTimeUtc(assemblyLocation); + } catch (Exception ex) { + LoggingService.Warn(ex); + } + + string fileName = XmlDoc.LookupLocalizedXmlDoc(assemblyLocation); + if (fileName == null) { + // Not found -> look in other directories: + foreach (string testDirectory in XmlDoc.XmlDocLookupDirectories) { + fileName = XmlDoc.LookupLocalizedXmlDoc(Path.Combine(testDirectory, Path.GetFileName(assemblyLocation))); + if (fileName != null) + break; + } + } + + if (fileName != null) { + if (registry.persistence != null) { + this.XmlDoc = XmlDoc.Load(fileName, Path.Combine(registry.persistence.CacheDirectory, "XmlDoc")); + } else { + this.XmlDoc = XmlDoc.Load(fileName, null); + } + } + } + + public void InitializeSpecialClasses() + { + // Replace the class representing System.Void with VoidClass.Instance + IClass voidClass = GetClassInternal(VoidClass.VoidName, 0, Language); + if (voidClass != null) { + RemoveClass(voidClass); + AddClassToNamespaceList(new VoidClass(this)); + } + } + + bool initialized = false; + List missingNames; + + public void InitializeReferences() + { + bool changed = false; + if (initialized) { + if (missingNames != null) { + for (int i = 0; i < missingNames.Count; i++) { + IProjectContent content = registry.GetExistingProjectContent(missingNames[i]); + if (content != null) { + changed = true; + lock (ReferencedContents) { + ReferencedContents.Add(content); + } + missingNames.RemoveAt(i--); + } + } + if (missingNames.Count == 0) { + missingNames = null; + } + } + } else { + initialized = true; + foreach (DomAssemblyName name in referencedAssemblyNames) { + IProjectContent content = registry.GetExistingProjectContent(name); + if (content != null) { + changed = true; + lock (ReferencedContents) { + ReferencedContents.Add(content); + } + } else { + if (missingNames == null) + missingNames = new List(); + missingNames.Add(name); + } + } + } + if (changed) + OnReferencedContentsChanged(EventArgs.Empty); + } + + public override string ToString() + { + return string.Format("[{0}: {1}]", GetType().Name, assemblyFullName); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReadOnlyDictionary.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReadOnlyDictionary.cs new file mode 100644 index 000000000..4b3b25b78 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReadOnlyDictionary.cs @@ -0,0 +1,99 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Wraps a IDictonary, allowing only the read-only operations. + /// + sealed class ReadOnlyDictionary : IDictionary + { + IDictionary baseDictionary; + + public ReadOnlyDictionary(IDictionary baseDictionary) + { + if (baseDictionary == null) + throw new ArgumentNullException("baseDictionary"); + this.baseDictionary = baseDictionary; + } + + public TValue this[TKey key] { + get { return baseDictionary[key]; } + set { throw new NotSupportedException(); } + } + + public ICollection Keys { + get { return baseDictionary.Keys; } + } + + public ICollection Values { + get { return baseDictionary.Values; } + } + + public int Count { + get { return baseDictionary.Count; } + } + + public bool IsReadOnly { + get { return true; } + } + + public bool ContainsKey(TKey key) + { + return baseDictionary.ContainsKey(key); + } + + public void Add(TKey key, TValue value) + { + throw new NotSupportedException(); + } + + public bool Remove(TKey key) + { + throw new NotSupportedException(); + } + + public bool TryGetValue(TKey key, out TValue value) + { + return baseDictionary.TryGetValue(key, out value); + } + + public void Add(KeyValuePair item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(KeyValuePair item) + { + return baseDictionary.Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotSupportedException(); + } + + public bool Remove(KeyValuePair item) + { + throw new NotSupportedException(); + } + + public IEnumerator> GetEnumerator() + { + return baseDictionary.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return baseDictionary.GetEnumerator(); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CSharpCodeGenerator.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CSharpCodeGenerator.cs new file mode 100644 index 000000000..aa13ab3f1 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CSharpCodeGenerator.cs @@ -0,0 +1,110 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.PrettyPrinter; + +namespace ICSharpCode.SharpDevelop.Dom.Refactoring +{ + public class CSharpCodeGenerator : NRefactoryCodeGenerator + { + internal static readonly CSharpCodeGenerator Instance = new CSharpCodeGenerator(); + + public override IOutputAstVisitor CreateOutputVisitor() + { + CSharpOutputVisitor v = new CSharpOutputVisitor(); + PrettyPrintOptions pOpt = v.Options; + + BraceStyle braceStyle; + if (this.Options.BracesOnSameLine) { + braceStyle = BraceStyle.EndOfLine; + } else { + braceStyle = BraceStyle.NextLine; + } + pOpt.StatementBraceStyle = braceStyle; + pOpt.EventAddBraceStyle = braceStyle; + pOpt.EventRemoveBraceStyle = braceStyle; + pOpt.PropertyBraceStyle = braceStyle; + pOpt.PropertyGetBraceStyle = braceStyle; + pOpt.PropertySetBraceStyle = braceStyle; + + pOpt.IndentationChar = this.Options.IndentString[0]; + pOpt.IndentSize = this.Options.IndentString.Length; + pOpt.TabSize = this.Options.IndentString.Length; + + return v; + } + + /// + /// Ensure that code is inserted correctly in {} code blocks - SD2-1180 + /// + public override void InsertCodeAtEnd(DomRegion region, IRefactoringDocument document, params AbstractNode[] nodes) + { + string beginLineIndentation = GetIndentation(document, region.BeginLine); + int insertionLine = region.EndLine - 1; + + IRefactoringDocumentLine endLine = document.GetLine(region.EndLine); + string endLineText = endLine.Text; + int originalPos = region.EndColumn - 2; // -1 for column coordinate => offset, -1 because EndColumn is after the '}' + int pos = originalPos; + if (pos < 0 || pos >= endLineText.Length || endLineText[pos] != '}') { + LoggingService.Warn("CSharpCodeGenerator.InsertCodeAtEnd: position is invalid (not pointing to '}')" + + " endLineText=" + endLineText + ", pos=" + pos); + } else { + for (pos--; pos >= 0; pos--) { + if (!char.IsWhiteSpace(endLineText[pos])) { + // range before '}' is not empty: we cannot simply insert in the line before the '}', so + // + pos++; // set pos to first whitespace character / the '{' character + if (pos < originalPos) { + // remove whitespace between last non-white character and the '}' + document.Remove(endLine.Offset + pos, originalPos - pos); + } + // insert newline and same indentation as used in beginLine before the '}' + document.Insert(endLine.Offset + pos, Environment.NewLine + beginLineIndentation); + insertionLine++; + + pos = region.BeginColumn - 1; + if (region.BeginLine == region.EndLine && pos >= 1 && pos < endLineText.Length) { + // The whole block was in on a single line, e.g. "get { return field; }". + // Insert an additional newline after the '{'. + + originalPos = pos = endLineText.IndexOf('{', pos); + if (pos >= 0 && pos < region.EndColumn - 1) { + // find next non-whitespace after originalPos + originalPos++; // point to insertion position for newline after { + for (pos++; pos < endLineText.Length; pos++) { + if (!char.IsWhiteSpace(endLineText[pos])) { + // remove all between originalPos and pos + if (originalPos < pos) { + document.Remove(endLine.Offset + originalPos, pos - originalPos); + } + document.Insert(endLine.Offset + originalPos, Environment.NewLine + beginLineIndentation + '\t'); + insertionLine++; + break; + } + } + } + } + break; + } + } + } + InsertCodeAfter(insertionLine, document, beginLineIndentation + this.Options.IndentString, nodes); + } + + public override PropertyDeclaration CreateProperty(IField field, bool createGetter, bool createSetter) + { + string propertyName = GetPropertyName(field.Name); + if (propertyName == field.Name && GetParameterName(propertyName) != propertyName) { + string newName = GetParameterName(propertyName); + if (HostCallback.RenameMember(field, newName)) { + field = new DefaultField(field.ReturnType, newName, + field.Modifiers, field.Region, field.DeclaringType); + } + } + return base.CreateProperty(field, createGetter, createSetter); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CodeGenerator.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CodeGenerator.cs new file mode 100644 index 000000000..e102176bc --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CodeGenerator.cs @@ -0,0 +1,868 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.AstBuilder; +using NR = ICSharpCode.NRefactory.Ast; + +namespace ICSharpCode.SharpDevelop.Dom.Refactoring +{ + /// + /// Provides code generation facilities. + /// + public abstract class CodeGenerator + { + protected CodeGenerator() + { + HostCallback.InitializeCodeGeneratorOptions(this); + } + + #region Dummy Code Generator + public static readonly CodeGenerator DummyCodeGenerator = new DummyCodeGeneratorClass(); + + private class DummyCodeGeneratorClass : CodeGenerator + { + public override string GenerateCode(AbstractNode node, string indentation) + { + return " - there is no code generator for this language - "; + } + } + #endregion + + #region DOM -> NRefactory conversion (static) + public static TypeReference ConvertType(IReturnType returnType, ClassFinder context) + { + if (returnType == null) return TypeReference.Null; + if (returnType is NullReturnType) return TypeReference.Null; + + ArrayReturnType arrayReturnType = returnType.CastToArrayReturnType(); + if (arrayReturnType != null) { + TypeReference typeRef = ConvertType(arrayReturnType.ArrayElementType, context); + int[] rank = typeRef.RankSpecifier ?? new int[0]; + Array.Resize(ref rank, rank.Length + 1); + rank[rank.Length - 1] = arrayReturnType.ArrayDimensions - 1; + typeRef.RankSpecifier = rank; + return typeRef; + } + PointerReturnType pointerReturnType = returnType.CastToDecoratingReturnType(); + if (pointerReturnType != null) { + TypeReference typeRef = ConvertType(pointerReturnType.BaseType, context); + typeRef.PointerNestingLevel++; + return typeRef; + } + + IList typeArguments = EmptyList.Instance; + if (returnType.IsConstructedReturnType) { + typeArguments = returnType.CastToConstructedReturnType().TypeArguments; + } + IClass c = returnType.GetUnderlyingClass(); + if (c != null) { + return CreateTypeReference(c, typeArguments, context); + } else { + TypeReference typeRef; + if (IsPrimitiveType(returnType)) + typeRef = new TypeReference(returnType.FullyQualifiedName, true); + else if (context != null && CanUseShortTypeName(returnType, context)) + typeRef = new TypeReference(returnType.Name); + else { + string fullName = returnType.FullyQualifiedName; + if (string.IsNullOrEmpty(fullName)) + fullName = returnType.Name; + typeRef = new TypeReference(fullName); + } + foreach (IReturnType typeArgument in typeArguments) { + typeRef.GenericTypes.Add(ConvertType(typeArgument, context)); + } + return typeRef; + } + } + + static TypeReference CreateTypeReference(IClass c, IList typeArguments, ClassFinder context) + { + if (c.DeclaringType != null) { + TypeReference outerClass = CreateTypeReference(c.DeclaringType, typeArguments, context); + List args = new List(); + for (int i = c.DeclaringType.TypeParameters.Count; i < Math.Min(c.TypeParameters.Count, typeArguments.Count); i++) { + args.Add(ConvertType(typeArguments[i], context)); + } + return new InnerClassTypeReference(outerClass, c.Name, args); + } else { + TypeReference typeRef; + if (IsPrimitiveType(c.DefaultReturnType)) + typeRef = new TypeReference(c.FullyQualifiedName, true); + else if (context != null && CanUseShortTypeName(c.DefaultReturnType, context)) + typeRef = new TypeReference(c.Name); + else + typeRef = new TypeReference(c.FullyQualifiedName); + for (int i = 0; i < Math.Min(c.TypeParameters.Count, typeArguments.Count); i++) { + typeRef.GenericTypes.Add(ConvertType(typeArguments[i], context)); + } + return typeRef; + } + } + + static bool IsPrimitiveType(IReturnType returnType) + { + return TypeReference.PrimitiveTypesCSharpReverse.ContainsKey(returnType.FullyQualifiedName); + } + + /// + /// Returns true if the short name of a type is valid in the given context. + /// Returns false for primitive types because they should be passed around using their + /// fully qualified names to allow the ambience or output visitor to use the intrinsic + /// type name. + /// + public static bool CanUseShortTypeName(IReturnType returnType, ClassFinder context) + { + if (returnType == null || context == null) + return false; + IReturnType typeInTargetContext = context.SearchType(returnType.Name, returnType.TypeArgumentCount); + return typeInTargetContext != null + && typeInTargetContext.FullyQualifiedName == returnType.FullyQualifiedName + && typeInTargetContext.TypeArgumentCount == returnType.TypeArgumentCount; + } + + public static Modifiers ConvertModifier(ModifierEnum modifiers, ClassFinder targetContext) + { + if (targetContext != null && targetContext.ProjectContent != null && targetContext.CallingClass != null) { + if (targetContext.ProjectContent.Language.IsClassWithImplicitlyStaticMembers(targetContext.CallingClass)) { + return ((Modifiers)modifiers) & ~Modifiers.Static; + } + } + if (modifiers.HasFlag(ModifierEnum.Static)) + modifiers &= ~(ModifierEnum.Abstract | ModifierEnum.Sealed); + return (Modifiers)modifiers; + } + + public static NR.ParameterModifiers ConvertModifier(Dom.ParameterModifiers m) + { + return (NR.ParameterModifiers)m; + } + + public static UsingDeclaration ConvertUsing(IUsing u) + { + List usings = new List(); + foreach (string name in u.Usings) { + usings.Add(new Using(name)); + } + if (u.HasAliases) { + foreach (KeyValuePair pair in u.Aliases) { + usings.Add(new Using(pair.Key, ConvertType(pair.Value, null))); + } + } + return new UsingDeclaration(usings); + } + + public static List ConvertParameters(IList parameters, ClassFinder targetContext) + { + List l = new List(parameters.Count); + foreach (IParameter p in parameters) { + ParameterDeclarationExpression pd = new ParameterDeclarationExpression(ConvertType(p.ReturnType, targetContext), + p.Name, + ConvertModifier(p.Modifiers)); + pd.Attributes = ConvertAttributes(p.Attributes, targetContext); + l.Add(pd); + } + return l; + } + + public static List ConvertAttributes(IList attributes, ClassFinder targetContext) + { + AttributeSection sec = new AttributeSection(); + foreach (IAttribute att in attributes) { + sec.Attributes.Add(new ICSharpCode.NRefactory.Ast.Attribute( + ConvertType(att.AttributeType, targetContext).Type, + att.PositionalArguments.Select(o => (Expression)new PrimitiveExpression(o)).ToList(), + att.NamedArguments.Select(p => new NamedArgumentExpression(p.Key, new PrimitiveExpression(p.Value))).ToList() + )); + } + List resultList = new List(1); + if (sec.Attributes.Count > 0) + resultList.Add(sec); + return resultList; + } + + public static List ConvertTemplates(IList l, ClassFinder targetContext) + { + List o = new List(l.Count); + foreach (ITypeParameter p in l) { + TemplateDefinition td = new TemplateDefinition(p.Name, ConvertAttributes(p.Attributes, targetContext)); + foreach (IReturnType rt in p.Constraints) { + td.Bases.Add(ConvertType(rt, targetContext)); + } + o.Add(td); + } + return o; + } + + public static BlockStatement CreateNotImplementedBlock() + { + BlockStatement b = new BlockStatement(); + b.Throw(new TypeReference("NotImplementedException").New()); + return b; + } + + public static AttributedNode ConvertMember(IMethod m, ClassFinder targetContext) + { + if (m.IsConstructor) { + return new ConstructorDeclaration(m.Name, + ConvertModifier(m.Modifiers, targetContext), + ConvertParameters(m.Parameters, targetContext), + ConvertAttributes(m.Attributes, targetContext)) { + Body = CreateNotImplementedBlock() + }; + } else if (m.Name == "#dtor") { // TODO : maybe add IsDestructor property? + return new DestructorDeclaration(m.Name, + ConvertModifier(m.Modifiers, targetContext), + ConvertAttributes(m.Attributes, targetContext)) { + Body = CreateNotImplementedBlock() + }; + } else { + return new MethodDeclaration { + Name = m.Name, + Modifier = ConvertModifier(m.Modifiers, targetContext), + TypeReference = ConvertType(m.ReturnType, targetContext), + Parameters = ConvertParameters(m.Parameters, targetContext), + Attributes = ConvertAttributes(m.Attributes, targetContext), + Templates = ConvertTemplates(m.TypeParameters, targetContext), + Body = m.Modifiers.HasFlag(ModifierEnum.Extern) ? null : CreateNotImplementedBlock(), + IsExtensionMethod = m.IsExtensionMethod, + InterfaceImplementations = ConvertInterfaceImplementations(m.InterfaceImplementations, targetContext) + }; + } + } + + public static List ConvertInterfaceImplementations(IEnumerable items, ClassFinder targetContext) + { + return items + .Select(i => new InterfaceImplementation(ConvertType(i.InterfaceReference, targetContext), i.MemberName)) + .ToList(); + } + + public static AttributedNode ConvertMember(IMember m, ClassFinder targetContext) + { + if (m == null) + throw new ArgumentNullException("m"); + if (m is IProperty) + return ConvertMember((IProperty)m, targetContext); + else if (m is IMethod) + return ConvertMember((IMethod)m, targetContext); + else if (m is IEvent) + return ConvertMember((IEvent)m, targetContext); + else if (m is IField) + return ConvertMember((IField)m, targetContext); + else + throw new ArgumentException("Unknown member: " + m.GetType().FullName); + } + + public static PropertyDeclaration ConvertMember(IProperty p, ClassFinder targetContext) + { + PropertyDeclaration md = new PropertyDeclaration(ConvertModifier(p.Modifiers, targetContext), + ConvertAttributes(p.Attributes, targetContext), + p.Name, + ConvertParameters(p.Parameters, targetContext)); + md.TypeReference = ConvertType(p.ReturnType, targetContext); + md.InterfaceImplementations = ConvertInterfaceImplementations(p.InterfaceImplementations, targetContext); + if (p.CanGet) { + md.GetRegion = new PropertyGetRegion(p.Modifiers.HasFlag(ModifierEnum.Extern) ? null : CreateNotImplementedBlock(), null); + md.GetRegion.Modifier = ConvertModifier(p.GetterModifiers, null); + } + if (p.CanSet) { + md.SetRegion = new PropertySetRegion(p.Modifiers.HasFlag(ModifierEnum.Extern) ? null : CreateNotImplementedBlock(), null); + md.SetRegion.Modifier = ConvertModifier(p.SetterModifiers, null); + } + return md; + } + + public static FieldDeclaration ConvertMember(IField f, ClassFinder targetContext) + { + TypeReference type = ConvertType(f.ReturnType, targetContext); + + FieldDeclaration fd = new FieldDeclaration(ConvertAttributes(f.Attributes, targetContext), + type, ConvertModifier(f.Modifiers, targetContext)); + + VariableDeclaration vd = new VariableDeclaration(f.Name, null, type); + fd.Fields.Add(vd); + + + if (f.IsConst && f.DeclaringType.ClassType != ClassType.Enum) + vd.Initializer = ExpressionBuilder.CreateDefaultValueForType(type); + else if (f.Modifiers.HasFlag(ModifierEnum.Fixed)) { + if (f.ReturnType.IsArrayReturnType) + fd.TypeReference = ConvertType(f.ReturnType.CastToArrayReturnType().ArrayElementType, targetContext); + vd.FixedArrayInitialization = new PrimitiveExpression(1); + } + + return fd; + } + + public static EventDeclaration ConvertMember(IEvent e, ClassFinder targetContext) + { + return new EventDeclaration { + TypeReference = ConvertType(e.ReturnType, targetContext), + Name = e.Name, + Modifier = ConvertModifier(e.Modifiers, targetContext), + Attributes = ConvertAttributes(e.Attributes, targetContext), + InterfaceImplementations = ConvertInterfaceImplementations(e.InterfaceImplementations, targetContext) + + }; + } + + public static AttributedNode ConvertClass(IClass c, ClassFinder targetContext) + { + if (c.ClassType == Dom.ClassType.Delegate) { + IMethod invoke = c.Methods.First(m => m.Name == "Invoke"); + + var d = new DelegateDeclaration(ConvertModifier(c.Modifiers, targetContext), ConvertAttributes(c.Attributes, targetContext)) { + Name = c.Name, + Parameters = ConvertParameters(invoke.Parameters, targetContext), + ReturnType = ConvertType(invoke.ReturnType, targetContext), + Templates = ConvertTemplates(c.TypeParameters, targetContext) + }; + + return d; + } else { + var t = new TypeDeclaration(ConvertModifier(c.Modifiers, targetContext), ConvertAttributes(c.Attributes, targetContext)) { + Type = (NRefactory.Ast.ClassType)c.ClassType, + BaseTypes = c.BaseTypes.Select(type => ConvertType(type, targetContext)).ToList(), + Templates = ConvertTemplates(c.TypeParameters, targetContext), + Name = c.Name + }; + + AttributedNode[] members = c.AllMembers.Select(m => ConvertMember(m, targetContext)).ToArray(); + + if (c.ClassType == ClassType.Interface) { + foreach (MethodDeclaration node in members.OfType()) { + node.Modifier &= ~(Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Internal); + node.Body = null; + } + foreach (PropertyDeclaration node in members.OfType()) { + node.Modifier &= ~(Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Internal); + node.GetRegion.Block = null; + node.SetRegion.Block = null; + } + foreach (EventDeclaration node in members.OfType()) { + node.Modifier &= ~(Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Internal); + } + } + + t.Children.AddRange(members); + t.Children.AddRange(c.InnerClasses.Select(c2 => ConvertClass(c2, targetContext))); + + return t; + } + } + #endregion + + readonly CodeGeneratorOptions options = new CodeGeneratorOptions(); + + public CodeGeneratorOptions Options { + get { return options; } + } + + #region Code generation / insertion + public virtual void InsertCodeAfter(IClass @class, IRefactoringDocument document, params AbstractNode[] nodes) + { + InsertCodeAfter(@class.BodyRegion.EndLine, document, + GetIndentation(document, @class.BodyRegion.BeginLine), nodes); + } + + public virtual void InsertCodeAfter(IMember member, IRefactoringDocument document, params AbstractNode[] nodes) + { + if (member is IMethodOrProperty) { + InsertCodeAfter(((IMethodOrProperty)member).BodyRegion.EndLine, document, + GetIndentation(document, member.Region.BeginLine), nodes); + } else { + int line = member.Region.EndLine; + // VB uses the position after the EOL as end location for fields, so insert after + // the previous line if the end position is pointing to the start of a line. + if (member.Region.EndColumn == 1) + line--; + InsertCodeAfter(line, document, + GetIndentation(document, member.Region.BeginLine), nodes); + } + } + + public virtual void InsertCodeAtEnd(DomRegion region, IRefactoringDocument document, params AbstractNode[] nodes) + { + InsertCodeAfter(region.EndLine - 1, document, + GetIndentation(document, region.BeginLine) + options.IndentString, nodes); + } + + public virtual void InsertCodeInClass(IClass c, IRefactoringDocument document, int targetLine, params AbstractNode[] nodes) + { + InsertCodeAfter(targetLine, document, + GetIndentation(document, c.Region.BeginLine) + options.IndentString, false, nodes); + } + + protected string GetIndentation(IRefactoringDocument document, int line) + { + string lineText = document.GetLine(line).Text; + return lineText.Substring(0, lineText.Length - lineText.TrimStart().Length); + } + + /// + /// Generates code for and inserts it into + /// after the line . + /// + protected void InsertCodeAfter(int insertLine, IRefactoringDocument document, string indentation, params AbstractNode[] nodes) + { + InsertCodeAfter(insertLine, document, indentation, true, nodes); + } + + /// + /// Generates code for and inserts it into + /// after the line . + /// + protected void InsertCodeAfter(int insertLine, IRefactoringDocument document, string indentation, bool startWithEmptyLine, params AbstractNode[] nodes) + { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < nodes.Length; i++) { + if (options.EmptyLinesBetweenMembers) { + if (startWithEmptyLine || i > 0) { + b.AppendLine(indentation); + } + } + b.Append(GenerateCode(nodes[i], indentation)); + } + if (insertLine < document.TotalNumberOfLines) { + IRefactoringDocumentLine lineSegment = document.GetLine(insertLine + 1); + document.Insert(lineSegment.Offset, b.ToString()); + } else { + b.Insert(0, Environment.NewLine); + document.Insert(document.TextLength, b.ToString()); + } + } + + /// + /// Generates code for the NRefactory node. + /// + public abstract string GenerateCode(AbstractNode node, string indentation); + #endregion + + #region Generate property + public virtual string GetPropertyName(string fieldName) + { + if (string.IsNullOrEmpty(fieldName)) + return fieldName; + if (fieldName.StartsWith("_") && fieldName.Length > 1) + return Char.ToUpper(fieldName[1]) + fieldName.Substring(2); + else if (fieldName.StartsWith("m_") && fieldName.Length > 2) + return Char.ToUpper(fieldName[2]) + fieldName.Substring(3); + else + return Char.ToUpper(fieldName[0]) + fieldName.Substring(1); + } + + public virtual string GetParameterName(string fieldName) + { + if (string.IsNullOrEmpty(fieldName)) + return fieldName; + if (fieldName.StartsWith("_") && fieldName.Length > 1) + return Char.ToLower(fieldName[1]) + fieldName.Substring(2); + else if (fieldName.StartsWith("m_") && fieldName.Length > 2) + return Char.ToLower(fieldName[2]) + fieldName.Substring(3); + else + return Char.ToLower(fieldName[0]) + fieldName.Substring(1); + } + + public virtual string GetFieldName(string propertyName) + { + if (string.IsNullOrEmpty(propertyName)) + return propertyName; + string newName = Char.ToLower(propertyName[0]) + propertyName.Substring(1); + if (newName == propertyName) + return "_" + newName; + else + return newName; + } + + public virtual PropertyDeclaration CreateProperty(IField field, bool createGetter, bool createSetter) + { + ClassFinder targetContext = new ClassFinder(field); + string name = GetPropertyName(field.Name); + PropertyDeclaration property = new PropertyDeclaration(ConvertModifier(field.Modifiers, targetContext), + null, + name, + null); + property.TypeReference = ConvertType(field.ReturnType, new ClassFinder(field)); + if (createGetter) { + BlockStatement block = new BlockStatement(); + block.Return(new IdentifierExpression(field.Name)); + property.GetRegion = new PropertyGetRegion(block, null); + } + if (createSetter) { + BlockStatement block = new BlockStatement(); + block.Assign(new IdentifierExpression(field.Name), new IdentifierExpression("value")); + property.SetRegion = new PropertySetRegion(block, null); + } + + property.Modifier = Modifiers.Public | (property.Modifier & Modifiers.Static); + return property; + } + #endregion + + #region Generate Changed Event + public virtual void CreateChangedEvent(IProperty property, IRefactoringDocument document) + { + ClassFinder targetContext = new ClassFinder(property); + string name = property.Name + "Changed"; + EventDeclaration ed = new EventDeclaration { + TypeReference = new TypeReference("EventHandler"), + Name = name, + Modifier = ConvertModifier(property.Modifiers & (ModifierEnum.VisibilityMask | ModifierEnum.Static), targetContext), + }; + InsertCodeAfter(property, document, ed); + + List arguments = new List(2); + if (property.IsStatic) + arguments.Add(new PrimitiveExpression(null, "null")); + else + arguments.Add(new ThisReferenceExpression()); + arguments.Add(new IdentifierExpression("EventArgs").Member("Empty")); + InsertCodeAtEnd(property.SetterRegion, document, + new RaiseEventStatement(name, arguments)); + } + #endregion + + #region Generate OnEventMethod + public virtual MethodDeclaration CreateOnEventMethod(IEvent e) + { + ClassFinder context = new ClassFinder(e); + List parameters = new List(); + bool sender = false; + if (e.ReturnType != null) { + IMethod invoke = e.ReturnType.GetMethods().Find(delegate(IMethod m) { return m.Name=="Invoke"; }); + if (invoke != null) { + foreach (IParameter param in invoke.Parameters) { + parameters.Add(new ParameterDeclarationExpression(ConvertType(param.ReturnType, context), param.Name)); + } + if (parameters.Count > 0 && string.Equals(parameters[0].ParameterName, "sender", StringComparison.InvariantCultureIgnoreCase)) { + sender = true; + parameters.RemoveAt(0); + } + } + } + + ModifierEnum modifier; + if (e.IsStatic) + modifier = ModifierEnum.Private | ModifierEnum.Static; + else if (e.DeclaringType.IsSealed) + modifier = ModifierEnum.Protected; + else + modifier = ModifierEnum.Protected | ModifierEnum.Virtual; + MethodDeclaration method = new MethodDeclaration { + Name = "On" + e.Name, + Modifier = ConvertModifier(modifier, context), + TypeReference = new TypeReference("System.Void", true), + Parameters = parameters + }; + + List arguments = new List(); + if (sender) { + if (e.IsStatic) + arguments.Add(new PrimitiveExpression(null, "null")); + else + arguments.Add(new ThisReferenceExpression()); + } + foreach (ParameterDeclarationExpression param in parameters) { + arguments.Add(new IdentifierExpression(param.ParameterName)); + } + method.Body = new BlockStatement(); + method.Body.AddChild(new RaiseEventStatement(e.Name, arguments)); + + return method; + } + #endregion + + #region Interface implementation + protected string GetInterfaceName(IReturnType interf, IMember member, ClassFinder context) + { + if (CanUseShortTypeName(member.DeclaringType.DefaultReturnType, context)) + return member.DeclaringType.Name; + else + return member.DeclaringType.FullyQualifiedName; + } + + public virtual void ImplementInterface(IReturnType interf, IRefactoringDocument document, bool explicitImpl, IClass targetClass) + { + List nodes = new List(); + ImplementInterface(nodes, interf, explicitImpl, targetClass); + InsertCodeAtEnd(targetClass.Region, document, nodes.ToArray()); + } + + static bool InterfaceMemberAlreadyImplementedParametersAreIdentical(IMember a, IMember b) + { + if (a is IMethodOrProperty && b is IMethodOrProperty) { + return DiffUtility.Compare(((IMethodOrProperty)a).Parameters, + ((IMethodOrProperty)b).Parameters) == 0; + } else { + return true; + } + } + + static T CloneAndAddExplicitImpl(T member, IClass targetClass) + where T : class, IMember + { + T copy = (T)member.Clone(); + copy.DeclaringTypeReference = targetClass.DefaultReturnType; + copy.InterfaceImplementations.Add(new ExplicitInterfaceImplementation(member.DeclaringTypeReference, member.Name)); + return copy; + } + + // FIXME this whole method could be probably replaced by DOM.ExtensionMethodsPublic.HasMember + public static bool InterfaceMemberAlreadyImplemented(IEnumerable existingMembers, T interfaceMember, + out bool requireAlternativeImplementation) + where T : class, IMember + { + IReturnType interf = interfaceMember.DeclaringTypeReference; + requireAlternativeImplementation = false; + foreach (T existing in existingMembers) { + StringComparer nameComparer = existing.DeclaringType.ProjectContent.Language.NameComparer; + + // if existing has same name as interfaceMember, and for methods the parameter list must also be identical: + if (nameComparer.Equals(existing.Name, interfaceMember.Name)) { + if (InterfaceMemberAlreadyImplementedParametersAreIdentical(existing, interfaceMember)) { + // implicit implementation found + if (object.Equals(existing.ReturnType, interfaceMember.ReturnType)) { + return true; + } else { + requireAlternativeImplementation = true; + } + } + } else { + foreach (ExplicitInterfaceImplementation eii in existing.InterfaceImplementations) { + if (object.Equals(eii.InterfaceReference, interf) && nameComparer.Equals(eii.MemberName, interfaceMember.Name)) { + if (InterfaceMemberAlreadyImplementedParametersAreIdentical(existing, interfaceMember)) { + // explicit implementation found + if (object.Equals(existing.ReturnType, interfaceMember.ReturnType)) { + return true; + } else { + requireAlternativeImplementation = true; + } + } + } + } + } + } + return false; + } + + static InterfaceImplementation CreateInterfaceImplementation(IMember interfaceMember, ClassFinder context) + { + return new InterfaceImplementation(ConvertType(interfaceMember.DeclaringTypeReference, context), interfaceMember.Name); + } + + /// + /// Adds the methods implementing the to the list + /// . + /// + public virtual void ImplementInterface(IList nodes, IReturnType interf, bool explicitImpl, IClass targetClass) + { + ClassFinder context = new ClassFinder(targetClass, targetClass.Region.BeginLine + 1, 0); + Modifiers implicitImplModifier = ConvertModifier(ModifierEnum.Public, context); + Modifiers explicitImplModifier = ConvertModifier(context.Language.ExplicitInterfaceImplementationIsPrivateScope ? ModifierEnum.None : ModifierEnum.Public, context); + List targetClassEvents = targetClass.DefaultReturnType.GetEvents(); + bool requireAlternativeImplementation; + foreach (IEvent e in interf.GetEvents()) { + if (!InterfaceMemberAlreadyImplemented(targetClassEvents, e, out requireAlternativeImplementation)) { + EventDeclaration ed = ConvertMember(e, context); + ed.Attributes.Clear(); + if (explicitImpl || requireAlternativeImplementation) { + ed.InterfaceImplementations.Add(CreateInterfaceImplementation(e, context)); + + if (context.Language.RequiresAddRemoveRegionInExplicitInterfaceImplementation) { + ed.AddRegion = new EventAddRegion(null); + ed.AddRegion.Block = CreateNotImplementedBlock(); + ed.RemoveRegion = new EventRemoveRegion(null); + ed.RemoveRegion.Block = CreateNotImplementedBlock(); + } + + targetClassEvents.Add(CloneAndAddExplicitImpl(e, targetClass)); + ed.Modifier = explicitImplModifier; + } else { + targetClassEvents.Add(e); + ed.Modifier = implicitImplModifier; + } + nodes.Add(ed); + } + } + List targetClassProperties = targetClass.DefaultReturnType.GetProperties(); + foreach (IProperty p in interf.GetProperties()) { + if (!InterfaceMemberAlreadyImplemented(targetClassProperties, p, out requireAlternativeImplementation)) { + AttributedNode pd = ConvertMember(p, context); + pd.Attributes.Clear(); + if (explicitImpl || requireAlternativeImplementation) { + InterfaceImplementation impl = CreateInterfaceImplementation(p, context); + ((PropertyDeclaration)pd).InterfaceImplementations.Add(impl); + targetClassProperties.Add(CloneAndAddExplicitImpl(p, targetClass)); + pd.Modifier = explicitImplModifier; + } else { + targetClassProperties.Add(p); + pd.Modifier = implicitImplModifier; + } + nodes.Add(pd); + } + } + List targetClassMethods = targetClass.DefaultReturnType.GetMethods(); + foreach (IMethod m in interf.GetMethods()) { + if (!InterfaceMemberAlreadyImplemented(targetClassMethods, m, out requireAlternativeImplementation)) { + MethodDeclaration md = ConvertMember(m, context) as MethodDeclaration; + md.Attributes.Clear(); + if (md != null) { + if (explicitImpl || requireAlternativeImplementation) { + md.InterfaceImplementations.Add(CreateInterfaceImplementation(m, context)); + targetClassMethods.Add(CloneAndAddExplicitImpl(m, targetClass)); + md.Modifier = explicitImplModifier; + } else { + targetClassMethods.Add(m); + md.Modifier = implicitImplModifier; + } + nodes.Add(md); + } + } + } + } + #endregion + + #region Abstract class implementation + public static void ImplementAbstractClass(IRefactoringDocument doc, IClass target, IReturnType abstractClass) + { + CodeGenerator generator = target.ProjectContent.Language.CodeGenerator; + var pos = doc.OffsetToPosition(doc.PositionToOffset(target.BodyRegion.EndLine, target.BodyRegion.EndColumn) - 1); + ClassFinder context = new ClassFinder(target, pos.Line, pos.Column); + + foreach (IMember member in MemberLookupHelper.GetAccessibleMembers(abstractClass, target, LanguageProperties.CSharp, true) + .Where(m => m.IsAbstract && !target.HasMember(m))) { + generator.InsertCodeAtEnd(target.BodyRegion, doc, generator.GetOverridingMethod(member, context)); + } + } + #endregion + + #region Override member + public virtual AttributedNode GetOverridingMethod(IMember baseMember, ClassFinder targetContext) + { + AbstractMember newMember = (AbstractMember)baseMember.Clone(); + newMember.Modifiers &= ~(ModifierEnum.Virtual | ModifierEnum.Abstract); + newMember.Modifiers |= ModifierEnum.Override; + // set modifiers be before calling convert so that a body is generated + AttributedNode node = ConvertMember(newMember, targetContext); + node.Attributes.Clear(); // don't copy over attributes + + if (!baseMember.IsAbstract) { + // replace the method/property body with a call to the base method/property + MethodDeclaration method = node as MethodDeclaration; + if (method != null) { + method.Body.Children.Clear(); + if (method.TypeReference.Type == "System.Void") { + method.Body.AddChild(new ExpressionStatement(CreateForwardingMethodCall(method))); + } else { + method.Body.AddChild(new ReturnStatement(CreateForwardingMethodCall(method))); + } + } + PropertyDeclaration property = node as PropertyDeclaration; + if (property != null) { + Expression field = new BaseReferenceExpression().Member(property.Name); + if (!property.GetRegion.Block.IsNull) { + property.GetRegion.Block.Children.Clear(); + property.GetRegion.Block.Return(field); + } + if (!property.SetRegion.Block.IsNull) { + property.SetRegion.Block.Children.Clear(); + property.SetRegion.Block.Assign(field, new IdentifierExpression("value")); + } + } + } + return node; + } + + static InvocationExpression CreateForwardingMethodCall(MethodDeclaration method) + { + Expression methodName = new MemberReferenceExpression(new BaseReferenceExpression(), + method.Name); + InvocationExpression ie = new InvocationExpression(methodName, null); + foreach (ParameterDeclarationExpression param in method.Parameters) { + Expression expr = new IdentifierExpression(param.ParameterName); + if (param.ParamModifier == NR.ParameterModifiers.Ref) { + expr = new DirectionExpression(FieldDirection.Ref, expr); + } else if (param.ParamModifier == NR.ParameterModifiers.Out) { + expr = new DirectionExpression(FieldDirection.Out, expr); + } + ie.Arguments.Add(expr); + } + return ie; + } + #endregion + + #region Using statements + public virtual void ReplaceUsings(IRefactoringDocument document, IList oldUsings, IList newUsings) + { + if (oldUsings.Count == newUsings.Count) { + bool identical = true; + for (int i = 0; i < oldUsings.Count; i++) { + if (oldUsings[i] != newUsings[i]) { + identical = false; + break; + } + } + if (identical) return; + } + + int firstLine = int.MaxValue; + List> regions = new List>(); + foreach (IUsing u in oldUsings) { + if (u.Region.BeginLine < firstLine) + firstLine = u.Region.BeginLine; + int st = document.PositionToOffset(u.Region.BeginLine, u.Region.BeginColumn); + int en = document.PositionToOffset(u.Region.EndLine, u.Region.EndColumn); + regions.Add(new KeyValuePair(st, en - st)); + } + + regions.Sort(delegate(KeyValuePair a, KeyValuePair b) { + return a.Key.CompareTo(b.Key); + }); + int insertionOffset = regions.Count == 0 ? 0 : regions[0].Key; + string indentation; + if (firstLine != int.MaxValue) { + indentation = GetIndentation(document, firstLine); + insertionOffset -= indentation.Length; + } else { + indentation = ""; + } + + document.StartUndoableAction(); + for (int i = regions.Count - 1; i >= 0; i--) { + document.Remove(regions[i].Key, regions[i].Value); + } + int lastNewLine = insertionOffset; + for (int i = insertionOffset; i < document.TextLength; i++) { + char c = document.GetCharAt(i); + if (!char.IsWhiteSpace(c)) + break; + if (c == '\n') { + if (i > 0 && document.GetCharAt(i - 1) == '\r') + lastNewLine = i - 1; + else + lastNewLine = i; + } + } + if (lastNewLine != insertionOffset) { + document.Remove(insertionOffset, lastNewLine - insertionOffset); + } + StringBuilder txt = new StringBuilder(); + foreach (IUsing us in newUsings) { + if (us == null) + txt.AppendLine(indentation); + else + txt.Append(GenerateCode(ConvertUsing(us), indentation)); + } + document.Insert(insertionOffset, txt.ToString()); + document.EndUndoableAction(); + } + #endregion + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CodeGeneratorOptions.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CodeGeneratorOptions.cs new file mode 100644 index 000000000..0d5e8d422 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CodeGeneratorOptions.cs @@ -0,0 +1,24 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom.Refactoring +{ + public class CodeGeneratorOptions + { + public bool BracesOnSameLine = true; + public bool EmptyLinesBetweenMembers = true; + string indentString = "\t"; + + public string IndentString { + get { return indentString; } + set { + if (string.IsNullOrEmpty(value)) { + throw new ArgumentNullException("value"); + } + indentString = value; + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/IRefactoringDocument.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/IRefactoringDocument.cs new file mode 100644 index 000000000..754a601c3 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/IRefactoringDocument.cs @@ -0,0 +1,125 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using ICSharpCode.NRefactory; +using System; + +namespace ICSharpCode.SharpDevelop.Dom.Refactoring +{ + /// + /// A document representing a source code file for refactoring. + /// Line and column counting starts at 1. + /// Offset counting starts at 0. + /// + public interface IRefactoringDocument : IServiceProvider + { + /// + /// Gets the total text length. + /// + /// The length of the text, in characters. + /// This is the same as Text.Length, but is more efficient because + /// it doesn't require creating a String object. + int TextLength { get; } + + /// + /// Gets the total number of lines in the document. + /// + int TotalNumberOfLines { get; } + + /// + /// Gets/Sets the whole text as string. + /// + string Text { get; set; } + + /// + /// Gets the document line with the specified number. + /// + /// The number of the line to retrieve. The first line has number 1. + IRefactoringDocumentLine GetLine(int lineNumber); + + /// + /// Gets the document line that contains the specified offset. + /// + IRefactoringDocumentLine GetLineForOffset(int offset); + + int PositionToOffset(int line, int column); + Location OffsetToPosition(int offset); + + void Insert(int offset, string text); + void Remove(int offset, int length); + void Replace(int offset, int length, string newText); + + /// + /// Gets a character at the specified position in the document. + /// + /// The index of the character to get. + /// Offset is outside the valid range (0 to TextLength-1). + /// The character at the specified position. + /// This is the same as Text[offset], but is more efficient because + /// it doesn't require creating a String object. + char GetCharAt(int offset); + + /// + /// Retrieves the text for a portion of the document. + /// + /// offset or length is outside the valid range. + /// This is the same as Text.Substring, but is more efficient because + /// it doesn't require creating a String object for the whole document. + string GetText(int offset, int length); + + /// + /// Make the document combine the following actions into a single + /// action for undo purposes. + /// + void StartUndoableAction(); + + /// + /// Ends the undoable action started with . + /// + void EndUndoableAction(); + + /// + /// Creates an undo group. Dispose the returned value to close the undo group. + /// + /// An object that closes the undo group when Dispose() is called. + IDisposable OpenUndoGroup(); + } + + /// + /// A line inside a . + /// + public interface IRefactoringDocumentLine + { + /// + /// Gets the starting offset of the line in the document's text. + /// + int Offset { get; } + + /// + /// Gets the length of this line (=the number of characters on the line). + /// + int Length { get; } + + /// + /// Gets the length of this line, including the line delimiter. + /// + int TotalLength { get; } + + /// + /// Gets the length of the line terminator. + /// Returns 1 or 2; or 0 at the end of the document. + /// + int DelimiterLength { get; } + + /// + /// Gets the number of this line. + /// The first line has the number 1. + /// + int LineNumber { get; } + + /// + /// Gets the text on this line. + /// + string Text { get; } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/NRefactoryCodeGenerator.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/NRefactoryCodeGenerator.cs new file mode 100644 index 000000000..f5a136f43 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/NRefactoryCodeGenerator.cs @@ -0,0 +1,34 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.PrettyPrinter; + +namespace ICSharpCode.SharpDevelop.Dom.Refactoring +{ + public abstract class NRefactoryCodeGenerator : CodeGenerator + { + public abstract IOutputAstVisitor CreateOutputVisitor(); + + public override string GenerateCode(AbstractNode node, string indentation) + { + IOutputAstVisitor visitor = CreateOutputVisitor(); + int indentCount = 0; + foreach (char c in indentation) { + if (c == '\t') + indentCount += 4; + else + indentCount += 1; + } + visitor.OutputFormatter.IndentationLevel = indentCount / 4; + if (node is Statement) + visitor.OutputFormatter.Indent(); + node.AcceptVisitor(visitor, null); + string text = visitor.Text; + if (node is Statement && !text.EndsWith("\n")) + text += Environment.NewLine; + return text; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/NRefactoryRefactoringProvider.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/NRefactoryRefactoringProvider.cs new file mode 100644 index 000000000..cb95efba5 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/NRefactoryRefactoringProvider.cs @@ -0,0 +1,635 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.PrettyPrinter; +using NR = ICSharpCode.NRefactory; + +namespace ICSharpCode.SharpDevelop.Dom.Refactoring +{ + public class NRefactoryRefactoringProvider : RefactoringProvider + { + public static readonly NRefactoryRefactoringProvider NRefactoryCSharpProviderInstance = new NRefactoryRefactoringProvider(NR.SupportedLanguage.CSharp); + public static readonly NRefactoryRefactoringProvider NRefactoryVBNetProviderInstance = new NRefactoryRefactoringProvider(NR.SupportedLanguage.VBNet); + + NR.SupportedLanguage language; + + private NRefactoryRefactoringProvider(NR.SupportedLanguage language) + { + this.language = language; + } + + public override bool IsEnabledForFile(string fileName) + { + string extension = Path.GetExtension(fileName); + if (extension.Equals(".cs", StringComparison.OrdinalIgnoreCase)) + return language == NR.SupportedLanguage.CSharp; + else if (extension.Equals(".vb", StringComparison.OrdinalIgnoreCase)) + return language == NR.SupportedLanguage.VBNet; + else + return false; + } + + static void ShowSourceCodeErrors(IDomProgressMonitor progressMonitor, string errors) + { + if (progressMonitor != null) + progressMonitor.ShowingDialog = true; + HostCallback.ShowMessage("${res:SharpDevelop.Refactoring.CannotPerformOperationBecauseOfSyntaxErrors}\n" + errors); + if (progressMonitor != null) + progressMonitor.ShowingDialog = false; + } + + NR.IParser ParseFile(IDomProgressMonitor progressMonitor, string fileContent) + { + NR.IParser parser = NR.ParserFactory.CreateParser(language, new StringReader(fileContent)); + parser.Parse(); + if (parser.Errors.Count > 0) { + ShowSourceCodeErrors(progressMonitor, parser.Errors.ErrorOutput); + parser.Dispose(); + return null; + } else { + return parser; + } + } + + IOutputAstVisitor GetOutputVisitor() { + switch (language) { + case NR.SupportedLanguage.CSharp: + return new CSharpOutputVisitor(); + case NR.SupportedLanguage.VBNet: + return new VBNetOutputVisitor(); + default: + throw new NotSupportedException(); + } + } + + string CommentToken { + get { + switch (language) { + case NR.SupportedLanguage.CSharp: + return "//"; + case NR.SupportedLanguage.VBNet: + return "'"; + default: + throw new NotSupportedException(); + } + } + } + + #region ExtractInterface + public override bool SupportsExtractInterface { + get { + return true; + } + } + + public override string GenerateInterfaceForClass(string newInterfaceName, string existingCode, IList membersToKeep, IClass sourceClass, bool preserveComments) + { + Modifiers modifiers = CodeGenerator.ConvertModifier(sourceClass.Modifiers, new ClassFinder(membersToKeep[0])); + // keep only visibility modifiers and 'unsafe' modifier + // -> remove abstract,sealed,static + modifiers &= Modifiers.Visibility | Modifiers.Unsafe; + + TypeDeclaration interfaceDef = new TypeDeclaration(modifiers, new List()); + interfaceDef.Name = newInterfaceName; + interfaceDef.Type = NR.Ast.ClassType.Interface; + interfaceDef.Templates = CodeGenerator.ConvertTemplates(sourceClass.TypeParameters, new ClassFinder(membersToKeep[0])); + + foreach (IMember member in membersToKeep) { + AttributedNode an = CodeGenerator.ConvertMember(member, new ClassFinder(member)); + INode node = null; + if (an is MethodDeclaration) { + MethodDeclaration m = an as MethodDeclaration; + m.Body = BlockStatement.Null; + m.Modifier = Modifiers.None; + node = m; + } else { + if (an is PropertyDeclaration) { + PropertyDeclaration p = an as PropertyDeclaration; + p.GetRegion.Block = BlockStatement.Null; + p.SetRegion.Block= BlockStatement.Null; + p.Modifier = Modifiers.None; + node = p; + } else { + if (an is EventDeclaration) { + EventDeclaration e = an as EventDeclaration; + e.Modifier = Modifiers.None; + node = e; + } + } + } + + if (node == null) + throw new NotSupportedException(); + + interfaceDef.AddChild(node); + } + + IOutputAstVisitor printer = this.GetOutputVisitor(); + + interfaceDef.AcceptVisitor(printer, null); + + string codeForNewInterface = printer.Text; + + // wrap the new code in the same comments/usings/namespace as the the original class file. + string newFileContent = CreateNewFileLikeExisting(existingCode, codeForNewInterface); + + return newFileContent; + } + + class AddTypeToBaseTypesVisitor : NR.Visitors.AbstractAstVisitor + { + IClass target, newBaseType; + + public AddTypeToBaseTypesVisitor(IClass target, IClass newBaseType) + { + this.target = target; + this.newBaseType = newBaseType; + } + + public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + { + // test the Type string property explicitly (rather than .BaseTypes.Contains()) + // to ensure that a matching type name is enough to prevent adding a second + // reference. + + if (typeDeclaration.Name != target.Name) + return base.VisitTypeDeclaration(typeDeclaration, data); + + bool exists = false; + foreach(TypeReference type in typeDeclaration.BaseTypes) { + if (type.Type == this.newBaseType.Name) { + exists = true; + break; + } + } + if (!exists) { + typeDeclaration.BaseTypes.Add(new TypeReference(newBaseType.Name, newBaseType.TypeParameters.Select(p => new TypeReference(p.Name)).ToList())); + } + return base.VisitTypeDeclaration(typeDeclaration, data); + } + } + + public override string AddBaseTypeToClass(string existingCode, IClass targetClass, IClass newBaseType) + { + NR.IParser parser = ParseFile(null, existingCode); + if (parser == null) { + return null; + } + + AddTypeToBaseTypesVisitor addTypeToBaseTypesVisitor = new AddTypeToBaseTypesVisitor(targetClass, newBaseType); + + parser.CompilationUnit.AcceptVisitor(addTypeToBaseTypesVisitor, null); + + // now use an output visitor for the appropriate language (based on + // extension of the existing code file) to format the new interface. + IOutputAstVisitor output = GetOutputVisitor(); + + // run the output visitor with the specials inserter to insert comments + using (SpecialNodesInserter.Install(parser.Lexer.SpecialTracker.RetrieveSpecials(), output)) { + parser.CompilationUnit.AcceptVisitor(output, null); + } + + parser.Dispose(); + + if (output.Errors.Count > 0) { + ShowSourceCodeErrors(null, output.Errors.ErrorOutput); + return null; + } + + return output.Text; + } + #endregion + + #region FindUnusedUsingDeclarations + protected class PossibleTypeReference + { + public string Name; + public int TypeParameterCount; + public IMethod ExtensionMethod; + + public PossibleTypeReference(string name) + { + this.Name = name; + } + + public PossibleTypeReference(IdentifierExpression identifierExpression) + { + this.Name = identifierExpression.Identifier; + this.TypeParameterCount = identifierExpression.TypeArguments.Count; + } + + public PossibleTypeReference(TypeReference tr) + { + this.Name = tr.Type; + this.TypeParameterCount = tr.GenericTypes.Count; + } + + public PossibleTypeReference(IMethod extensionMethod) + { + this.ExtensionMethod = extensionMethod; + } + + public override int GetHashCode() + { + int hashCode = 0; + unchecked { + if (Name != null) hashCode += 1000000007 * Name.GetHashCode(); + hashCode += 1000000009 * TypeParameterCount.GetHashCode(); + if (ExtensionMethod != null) hashCode += 1000000021 * ExtensionMethod.GetHashCode(); + } + return hashCode; + } + + public override bool Equals(object obj) + { + PossibleTypeReference other = obj as PossibleTypeReference; + if (other == null) return false; + return this.Name == other.Name && this.TypeParameterCount == other.TypeParameterCount && object.Equals(this.ExtensionMethod, other.ExtensionMethod); + } + } + + private class FindPossibleTypeReferencesVisitor : NR.Visitors.AbstractAstVisitor + { + internal HashSet list = new HashSet(); + NRefactoryResolver.NRefactoryResolver resolver; + ParseInformation parseInformation; + + public FindPossibleTypeReferencesVisitor(ParseInformation parseInformation) + { + if (parseInformation != null) { + this.parseInformation = parseInformation; + resolver = new NRefactoryResolver.NRefactoryResolver(parseInformation.CompilationUnit.ProjectContent.Language); + } + } + + public override object VisitIdentifierExpression(IdentifierExpression identifierExpression, object data) + { + list.Add(new PossibleTypeReference(identifierExpression)); + return base.VisitIdentifierExpression(identifierExpression, data); + } + + public override object VisitTypeReference(TypeReference typeReference, object data) + { + if (!typeReference.IsGlobal) { + list.Add(new PossibleTypeReference(typeReference)); + } + return base.VisitTypeReference(typeReference, data); + } + + public override object VisitAttribute(ICSharpCode.NRefactory.Ast.Attribute attribute, object data) + { + list.Add(new PossibleTypeReference(attribute.Name)); + list.Add(new PossibleTypeReference(attribute.Name + "Attribute")); + return base.VisitAttribute(attribute, data); + } + + public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data) + { + base.VisitInvocationExpression(invocationExpression, data); + // don't use uninitialized resolver + if (resolver.ProjectContent == null) + return null; + if (invocationExpression.TargetObject is MemberReferenceExpression) { + MemberResolveResult mrr = resolver.ResolveInternal(invocationExpression, ExpressionContext.Default) as MemberResolveResult; + if (mrr != null) { + IMethod method = mrr.ResolvedMember as IMethod; + if (method != null && method.IsExtensionMethod) { + list.Add(new PossibleTypeReference(method)); + } + } + } + return null; + } + + public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) + { + // Initialize resolver for method: + if (!methodDeclaration.Body.IsNull && resolver != null) { + if (resolver.Initialize(parseInformation, methodDeclaration.Body.StartLocation.Y, methodDeclaration.Body.StartLocation.X)) { + resolver.RunLookupTableVisitor(methodDeclaration); + } + } + return base.VisitMethodDeclaration(methodDeclaration, data); + } + + public override object VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data) + { + if (resolver != null) { + if (resolver.Initialize(parseInformation, propertyDeclaration.BodyStart.Y, propertyDeclaration.BodyStart.X)) { + resolver.RunLookupTableVisitor(propertyDeclaration); + } + } + return base.VisitPropertyDeclaration(propertyDeclaration, data); + } + + public override object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data) + { + if (resolver != null) { + resolver.Initialize(parseInformation, fieldDeclaration.StartLocation.X, fieldDeclaration.StartLocation.Y); + } + return base.VisitFieldDeclaration(fieldDeclaration, data); + } + } + + protected virtual HashSet FindPossibleTypeReferences(IDomProgressMonitor progressMonitor, string fileContent, ParseInformation parseInfo) + { + NR.IParser parser = ParseFile(progressMonitor, fileContent); + if (parser == null) { + return null; + } else { + FindPossibleTypeReferencesVisitor visitor = new FindPossibleTypeReferencesVisitor(parseInfo); + parser.CompilationUnit.AcceptVisitor(visitor, null); + parser.Dispose(); + return visitor.list; + } + } + + public override bool SupportsFindUnusedUsingDeclarations { + get { + return true; + } + } + + public override IList FindUnusedUsingDeclarations(IDomProgressMonitor progressMonitor, string fileName, string fileContent, ICompilationUnit cu) + { + IClass @class = cu.Classes.Count == 0 ? null : cu.Classes[0]; + + HashSet references = FindPossibleTypeReferences(progressMonitor, fileContent, new ParseInformation(cu)); + if (references == null) return new IUsing[0]; + + HashSet usedUsings = new HashSet(); + foreach (PossibleTypeReference tr in references) { + if (tr.ExtensionMethod != null) { + // the invocation of an extension method can implicitly use a using + StringComparer nameComparer = cu.ProjectContent.Language.NameComparer; + // go through all usings in all nested child scopes + foreach (IUsing import in cu.GetAllUsings()) { + foreach (string i in import.Usings) { + if (nameComparer.Equals(tr.ExtensionMethod.DeclaringType.Namespace, i)) { + usedUsings.Add(import); + } + } + } + } else { + // normal possible type reference + SearchTypeRequest request = new SearchTypeRequest(tr.Name, tr.TypeParameterCount, @class, cu, 1, 1); + SearchTypeResult response = cu.ProjectContent.SearchType(request); + if (response.UsedUsing != null) { + usedUsings.Add(response.UsedUsing); + } + } + } + + List unusedUsings = new List(); + foreach (IUsing import in cu.GetAllUsings()) { + if (!usedUsings.Contains(import)) { + if (import.HasAliases) { + foreach (string key in import.Aliases.Keys) { + if (references.Contains(new PossibleTypeReference(key))) + goto checkNextImport; + } + } + unusedUsings.Add(import); // this using is unused + } + checkNextImport:; + } + return unusedUsings; + } + #endregion + + #region CreateNewFileLikeExisting + public override bool SupportsCreateNewFileLikeExisting { + get { + return true; + } + } + + public override string CreateNewFileLikeExisting(string existingFileContent, string codeForNewType) + { + NR.IParser parser = ParseFile(null, existingFileContent); + if (parser == null) { + return null; + } + RemoveTypesVisitor visitor = new RemoveTypesVisitor(); + parser.CompilationUnit.AcceptVisitor(visitor, null); + List comments = new List(); + foreach (NR.ISpecial c in parser.Lexer.SpecialTracker.CurrentSpecials) { + if (c.StartPosition.Y <= visitor.includeCommentsUpToLine + || c.StartPosition.Y > visitor.includeCommentsAfterLine) + { + comments.Add(c); + } + } + IOutputAstVisitor outputVisitor = (language==NR.SupportedLanguage.CSharp) ? new CSharpOutputVisitor() : (IOutputAstVisitor)new VBNetOutputVisitor(); + using (SpecialNodesInserter.Install(comments, outputVisitor)) { + parser.CompilationUnit.AcceptVisitor(outputVisitor, null); + } + string expectedText; + if (language==NR.SupportedLanguage.CSharp) + expectedText = "using " + RemoveTypesVisitor.DummyIdentifier + ";"; + else + expectedText = "Imports " + RemoveTypesVisitor.DummyIdentifier; + using (StringWriter w = new StringWriter()) { + using (StringReader r1 = new StringReader(outputVisitor.Text)) { + string line; + while ((line = r1.ReadLine()) != null) { + string trimLine = line.TrimStart(); + if (trimLine == expectedText) { + string indentation = line.Substring(0, line.Length - trimLine.Length); + using (StringReader r2 = new StringReader(codeForNewType)) { + while ((line = r2.ReadLine()) != null) { + w.Write(indentation); + w.WriteLine(line); + } + } + } else { + w.WriteLine(line); + } + } + } + if (visitor.firstType) { + w.WriteLine(codeForNewType); + } + return w.ToString(); + } + } + + private class RemoveTypesVisitor : NR.Visitors.AbstractAstTransformer + { + internal const string DummyIdentifier = "DummyNamespace!InsertionPos"; + + internal int includeCommentsUpToLine; + internal int includeCommentsAfterLine = int.MaxValue; + + internal bool firstType = true; + + public override object VisitUsingDeclaration(UsingDeclaration usingDeclaration, object data) + { + if (firstType) { + includeCommentsUpToLine = usingDeclaration.EndLocation.Y; + } + return null; + } + + public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data) + { + includeCommentsAfterLine = namespaceDeclaration.EndLocation.Y; + if (firstType) { + includeCommentsUpToLine = namespaceDeclaration.StartLocation.Y; + return base.VisitNamespaceDeclaration(namespaceDeclaration, data); + } else { + RemoveCurrentNode(); + return null; + } + } + + void HandleTypeDeclaration(AttributedNode typeDeclaration) + { + if (typeDeclaration.EndLocation.Y > includeCommentsAfterLine) + includeCommentsAfterLine = typeDeclaration.EndLocation.Y; + if (firstType) { + firstType = false; + ReplaceCurrentNode(new UsingDeclaration(DummyIdentifier)); + } else { + RemoveCurrentNode(); + } + } + + public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + { + HandleTypeDeclaration(typeDeclaration); + return null; + } + + public override object VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration, object data) + { + HandleTypeDeclaration(delegateDeclaration); + return null; + } + } + #endregion + + #region ExtractCodeForType + public override bool SupportsGetFullCodeRangeForType { + get { + return true; + } + } + + public override DomRegion GetFullCodeRangeForType(string fileContent, IClass type) + { + NR.Parser.ILexer lexer = NR.ParserFactory.CreateLexer(language, new StringReader(fileContent)); + // use the lexer to determine last token position before type start + // and next token position after type end + Stack stack = new Stack(); + NR.Location lastPos = NR.Location.Empty; + NR.Parser.Token t = lexer.NextToken(); + bool csharp = language == NR.SupportedLanguage.CSharp; + int eof = csharp ? NR.Parser.CSharp.Tokens.EOF : NR.Parser.VB.Tokens.EOF; + int attribStart = csharp ? NR.Parser.CSharp.Tokens.OpenSquareBracket : NR.Parser.VB.Tokens.LessThan; + int attribEnd = csharp ? NR.Parser.CSharp.Tokens.CloseSquareBracket : NR.Parser.VB.Tokens.GreaterThan; + + while (t.Kind != eof) { + if (t.Kind == attribStart) + stack.Push(lastPos); + if (t.EndLocation.Y >= type.Region.BeginLine) + break; + lastPos = t.EndLocation; + if (t.Kind == attribEnd && stack.Count > 0) + lastPos = stack.Pop(); + t = lexer.NextToken(); + } + + stack = null; + + // Skip until end of type + while (t.Kind != eof) { + if (t.EndLocation.Y > type.BodyRegion.EndLine) + break; + t = lexer.NextToken(); + } + + int lastLineBefore = lastPos.IsEmpty ? 0 : lastPos.Y; + int firstLineAfter = t.EndLocation.IsEmpty ? int.MaxValue : t.EndLocation.Y; + + lexer.Dispose(); + lexer = null; + + StringReader myReader = new StringReader(fileContent); + + string line; + string mainLine; + int resultBeginLine = lastLineBefore + 1; + int resultEndLine = firstLineAfter - 1; + int lineNumber = 0; + int largestEmptyLineCount = 0; + int emptyLinesInRow = 0; + while ((line = myReader.ReadLine()) != null) { + lineNumber++; + if (lineNumber <= lastLineBefore) + continue; + if (lineNumber < type.Region.BeginLine) { + string trimLine = line.TrimStart(); + if (trimLine.Length == 0) { + if (++emptyLinesInRow > largestEmptyLineCount) { + largestEmptyLineCount = emptyLinesInRow; + resultBeginLine = lineNumber + 1; + } + } else { + emptyLinesInRow = 0; + if (IsEndDirective(trimLine)) { + largestEmptyLineCount = 0; + resultBeginLine = lineNumber + 1; + } + } + } else if (lineNumber == type.Region.BeginLine) { + mainLine = line; + } + // Region.BeginLine could be BodyRegion.EndLine + if (lineNumber == type.BodyRegion.EndLine) { + largestEmptyLineCount = 0; + emptyLinesInRow = 0; + resultEndLine = lineNumber; + } else if (lineNumber > type.BodyRegion.EndLine) { + if (lineNumber >= firstLineAfter) + break; + string trimLine = line.TrimStart(); + if (trimLine.Length == 0) { + if (++emptyLinesInRow > largestEmptyLineCount) { + largestEmptyLineCount = emptyLinesInRow; + resultEndLine = lineNumber - emptyLinesInRow; + } + } else { + emptyLinesInRow = 0; + if (IsStartDirective(trimLine)) { + break; + } + } + } + } + + myReader.Dispose(); + return new DomRegion(resultBeginLine, 0, resultEndLine, int.MaxValue); + } + + static bool IsEndDirective(string trimLine) + { + return trimLine.StartsWith("#endregion", StringComparison.Ordinal) + || trimLine.StartsWith("#endif", StringComparison.Ordinal); + } + + static bool IsStartDirective(string trimLine) + { + return trimLine.StartsWith("#region", StringComparison.Ordinal) + || trimLine.StartsWith("#if", StringComparison.Ordinal); + } + #endregion + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/RefactoringProvider.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/RefactoringProvider.cs new file mode 100644 index 000000000..1516324e3 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/RefactoringProvider.cs @@ -0,0 +1,93 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using ICSharpCode.NRefactory.Ast; +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom.Refactoring +{ + public abstract class RefactoringProvider + { + /// + /// A RefactoringProvider instance that supports no refactorings. + /// + public static readonly RefactoringProvider DummyProvider = new DummyRefactoringProvider(); + + protected RefactoringProvider() {} + + public abstract bool IsEnabledForFile(string fileName); + + private class DummyRefactoringProvider : RefactoringProvider + { + public override bool IsEnabledForFile(string fileName) + { + return false; + } + } + + #region ExtractInterface + public virtual bool SupportsExtractInterface { + get { + return false; + } + } + public virtual string GenerateInterfaceForClass(string newInterfaceName, string existingCode, IList membersToKeep, IClass sourceClass, bool preserveComments) + + { + throw new NotSupportedException(); + } + + public virtual string AddBaseTypeToClass(string existingCode, IClass targetClass, IClass newBaseType) + { + throw new NotImplementedException(); + } + #endregion + + #region FindUnusedUsingDeclarations + public virtual bool SupportsFindUnusedUsingDeclarations { + get { + return false; + } + } + + public virtual IList FindUnusedUsingDeclarations(IDomProgressMonitor progressMonitor, string fileName, string fileContent, ICompilationUnit compilationUnit) + { + throw new NotSupportedException(); + } + #endregion + + #region CreateNewFileLikeExisting + public virtual bool SupportsCreateNewFileLikeExisting { + get { + return false; + } + } + + /// + /// Creates a new file that uses same header, usings and namespace like an existing file. + /// + /// the content for the new file, + /// or null if an error occurred (error will be displayed to the user) + /// Content of the exisiting file + /// Code to put in the new file. + public virtual string CreateNewFileLikeExisting(string existingFileContent, string codeForNewType) + { + throw new NotSupportedException(); + } + #endregion + + #region ExtractCodeForType + public virtual bool SupportsGetFullCodeRangeForType { + get { + return false; + } + } + + public virtual DomRegion GetFullCodeRangeForType(string fileContent, IClass type) + { + throw new NotSupportedException(); + } + #endregion + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/TextFinder.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/TextFinder.cs new file mode 100644 index 000000000..5073d89c8 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/TextFinder.cs @@ -0,0 +1,41 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom.Refactoring +{ + public struct TextFinderMatch + { + public readonly int Position; + public readonly int Length; + + public readonly int ResolvePosition; + + public static readonly TextFinderMatch Empty = new TextFinderMatch(-1, 0); + + public TextFinderMatch(int position, int length) + { + this.Position = position; + this.Length = length; + this.ResolvePosition = position; + } + + public TextFinderMatch(int position, int length, int resolvePosition) + { + this.Position = position; + this.Length = length; + this.ResolvePosition = resolvePosition; + } + } + + public abstract class TextFinder + { + public virtual string PrepareInputText(string inputText) + { + return inputText; + } + + public abstract TextFinderMatch Find(string inputText, int startPosition); + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/VBNetCodeGenerator.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/VBNetCodeGenerator.cs new file mode 100644 index 000000000..ef7ee585f --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/VBNetCodeGenerator.cs @@ -0,0 +1,43 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.PrettyPrinter; + +namespace ICSharpCode.SharpDevelop.Dom.Refactoring +{ + public class VBNetCodeGenerator : NRefactoryCodeGenerator + { + internal static readonly VBNetCodeGenerator Instance = new VBNetCodeGenerator(); + + public override IOutputAstVisitor CreateOutputVisitor() + { + VBNetOutputVisitor v = new VBNetOutputVisitor(); + VBNetPrettyPrintOptions pOpt = v.Options; + + pOpt.IndentationChar = this.Options.IndentString[0]; + pOpt.IndentSize = this.Options.IndentString.Length; + pOpt.TabSize = this.Options.IndentString.Length; + + return v; + } + + public override string GetFieldName(string propertyName) + { + return "m_" + propertyName; + } + + public override PropertyDeclaration CreateProperty(IField field, bool createGetter, bool createSetter) + { + string propertyName = GetPropertyName(field.Name); + if (string.Equals(propertyName, field.Name, StringComparison.InvariantCultureIgnoreCase)) { + if (HostCallback.RenameMember(field, "m_" + field.Name)) { + field = new DefaultField(field.ReturnType, "m_" + field.Name, + field.Modifiers, field.Region, field.DeclaringType); + } + } + return base.CreateProperty(field, createGetter, createSetter); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/DomPersistence.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/DomPersistence.cs new file mode 100644 index 000000000..a7c12bf98 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/DomPersistence.cs @@ -0,0 +1,1060 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Reflection; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// This class can write Dom entity into a binary file for fast loading. + /// + public sealed class DomPersistence + { + public const long FileMagic = 0x11635233ED2F428C; + public const long IndexFileMagic = 0x11635233ED2F427D; + public const short FileVersion = 28; + + ProjectContentRegistry registry; + string cacheDirectory; + + internal string CacheDirectory { + get { + return cacheDirectory; + } + } + + internal DomPersistence(string cacheDirectory, ProjectContentRegistry registry) + { + this.cacheDirectory = cacheDirectory; + this.registry = registry; + + cacheIndex = LoadCacheIndex(); + } + + #region Cache management + public string SaveProjectContent(ReflectionProjectContent pc) + { + // create cache directory, if necessary + Directory.CreateDirectory(cacheDirectory); + + string assemblyFullName = pc.AssemblyFullName; + int pos = assemblyFullName.IndexOf(','); + string fileName = Path.Combine(cacheDirectory, + assemblyFullName.Substring(0, pos) + + "." + pc.AssemblyLocation.GetHashCode().ToString("x", CultureInfo.InvariantCulture) + + ".dat"); + AddFileNameToCacheIndex(Path.GetFileName(fileName), pc); + using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) { + WriteProjectContent(pc, fs); + } + return fileName; + } + + public ReflectionProjectContent LoadProjectContentByAssemblyName(string assemblyName) + { + string cacheFileName; + if (CacheIndex.TryGetValue(assemblyName, out cacheFileName)) { + cacheFileName = Path.Combine(cacheDirectory, cacheFileName); + if (File.Exists(cacheFileName)) { + return LoadProjectContent(cacheFileName); + } + } + return null; + } + + public ReflectionProjectContent LoadProjectContent(string cacheFileName) + { + using (FileStream fs = new FileStream(cacheFileName, FileMode.Open, FileAccess.Read, + FileShare.Read | FileShare.Delete, 4096, FileOptions.SequentialScan)) { + return LoadProjectContent(fs); + } + } + #endregion + + #region Cache index + string GetIndexFileName() { return Path.Combine(cacheDirectory, "index.dat"); } + + Dictionary cacheIndex; + + Dictionary CacheIndex { + get { + return cacheIndex; + } + } + + Dictionary LoadCacheIndex() + { + string indexFile = GetIndexFileName(); + Dictionary list = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (File.Exists(indexFile)) { + try { + using (FileStream fs = new FileStream(indexFile, FileMode.Open, FileAccess.Read)) { + using (BinaryReader reader = new BinaryReader(fs)) { + if (reader.ReadInt64() != IndexFileMagic) { + LoggingService.Warn("Index cache has wrong file magic"); + return list; + } + if (reader.ReadInt16() != FileVersion) { + LoggingService.Warn("Index cache has wrong file version"); + return list; + } + int count = reader.ReadInt32(); + for (int i = 0; i < count; i++) { + string key = reader.ReadString(); + list[key] = reader.ReadString(); + } + } + } + } catch (IOException ex) { + LoggingService.Warn("Error reading DomPersistance cache index", ex); + } + } + return list; + } + + void SaveCacheIndex(Dictionary cacheIndex) + { + string indexFile = GetIndexFileName(); + using (FileStream fs = new FileStream(indexFile, FileMode.Create, FileAccess.Write)) { + using (BinaryWriter writer = new BinaryWriter(fs)) { + writer.Write(IndexFileMagic); + writer.Write(FileVersion); + writer.Write(cacheIndex.Count); + foreach (KeyValuePair e in cacheIndex) { + writer.Write(e.Key); + writer.Write(e.Value); + } + } + } + } + + void AddFileNameToCacheIndex(string cacheFile, ReflectionProjectContent pc) + { + Dictionary l = LoadCacheIndex(); + l[pc.AssemblyLocation] = cacheFile; + string txt = pc.AssemblyFullName; + l[txt] = cacheFile; + int pos = txt.LastIndexOf(','); + do { + txt = txt.Substring(0, pos); + if (l.ContainsKey(txt)) + break; + l[txt] = cacheFile; + pos = txt.LastIndexOf(','); + } while (pos >= 0); + SaveCacheIndex(l); + cacheIndex = l; + } + #endregion + + #region Saving / Loading without cache + /// + /// Saves the project content to the stream. + /// + public static void WriteProjectContent(ReflectionProjectContent pc, Stream stream) + { + BinaryWriter writer = new BinaryWriter(stream); + new ReadWriteHelper(writer).WriteProjectContent(pc); + // do not close the stream + } + + /// + /// Load a project content from a stream. + /// + public ReflectionProjectContent LoadProjectContent(Stream stream) + { + return LoadProjectContent(stream, registry); + } + + public static ReflectionProjectContent LoadProjectContent(Stream stream, ProjectContentRegistry registry) + { + ReflectionProjectContent pc; + BinaryReader reader = new BinaryReader(stream); + try { + pc = new ReadWriteHelper(reader).ReadProjectContent(registry); + if (pc != null) { + pc.InitializeSpecialClasses(); + pc.AssemblyCompilationUnit.Freeze(); + } + return pc; + } catch (EndOfStreamException) { + LoggingService.Warn("Read dom: EndOfStreamException"); + return null; + } catch (Exception ex) { + HostCallback.ShowMessage("Error loading cached code-completion data.\n" + + "The cached file might be corrupted and will be regenerated.\n\n" + + ex.ToString()); + return null; + } + // do not close the stream + } + #endregion + + private struct ClassNameTypeCountPair + { + public readonly string ClassName; + public readonly byte TypeParameterCount; + + public ClassNameTypeCountPair(IClass c) { + this.ClassName = c.FullyQualifiedName; + this.TypeParameterCount = (byte)c.TypeParameters.Count; + } + + public ClassNameTypeCountPair(IReturnType rt) { + this.ClassName = rt.FullyQualifiedName; + this.TypeParameterCount = (byte)rt.TypeArgumentCount; + } + + public override bool Equals(object obj) { + if (!(obj is ClassNameTypeCountPair)) return false; + ClassNameTypeCountPair myClassNameTypeCountPair = (ClassNameTypeCountPair)obj; + if (ClassName != myClassNameTypeCountPair.ClassName) return false; + if (TypeParameterCount != myClassNameTypeCountPair.TypeParameterCount) return false; + return true; + } + + public override int GetHashCode() { + return ClassName.GetHashCode() ^ ((int)TypeParameterCount * 5); + } + } + + private sealed class ReadWriteHelper + { + ReflectionProjectContent pc; + + // for writing: + readonly BinaryWriter writer; + readonly Dictionary classIndices = new Dictionary(); + readonly Dictionary stringDict = new Dictionary(); + + // for reading: + readonly BinaryReader reader; + IReturnType[] types; + string[] stringArray; + + #region Write/Read ProjectContent + public ReadWriteHelper(BinaryWriter writer) + { + this.writer = writer; + } + + public void WriteProjectContent(ReflectionProjectContent pc) + { + this.pc = pc; + writer.Write(FileMagic); + writer.Write(FileVersion); + writer.Write(pc.AssemblyFullName); + writer.Write(pc.AssemblyLocation); + long time = 0; + try { + time = File.GetLastWriteTimeUtc(pc.AssemblyLocation).ToFileTime(); + } catch {} + writer.Write(time); + writer.Write(pc.ReferencedAssemblyNames.Count); + foreach (DomAssemblyName name in pc.ReferencedAssemblyNames) { + writer.Write(name.FullName); + } + WriteClasses(); + } + + public ReadWriteHelper(BinaryReader reader) + { + this.reader = reader; + } + + public ReflectionProjectContent ReadProjectContent(ProjectContentRegistry registry) + { + if (reader.ReadInt64() != FileMagic) { + LoggingService.Warn("Read dom: wrong magic"); + return null; + } + if (reader.ReadInt16() != FileVersion) { + LoggingService.Warn("Read dom: wrong version"); + return null; + } + string assemblyName = reader.ReadString(); + string assemblyLocation = reader.ReadString(); + long time = 0; + try { + time = File.GetLastWriteTimeUtc(assemblyLocation).ToFileTime(); + } catch {} + if (reader.ReadInt64() != time) { + LoggingService.Warn("Read dom: assembly changed since cache was created"); + return null; + } + DomAssemblyName[] referencedAssemblies = new DomAssemblyName[reader.ReadInt32()]; + for (int i = 0; i < referencedAssemblies.Length; i++) { + referencedAssemblies[i] = new DomAssemblyName(reader.ReadString()); + } + this.pc = new ReflectionProjectContent(assemblyName, assemblyLocation, referencedAssemblies, registry); + if (ReadClasses()) { + return pc; + } else { + LoggingService.Warn("Read dom: error in file (invalid control mark)"); + return null; + } + } + + void WriteClasses() + { + ICollection classes = pc.Classes; + IList assemblyAttributes = pc.GetAssemblyAttributes(); + + classIndices.Clear(); + stringDict.Clear(); + int i = 0; + foreach (IClass c in classes) { + classIndices[new ClassNameTypeCountPair(c)] = i; + i += 1; + } + + List externalTypes = new List(); + List stringList = new List(); + CreateExternalTypeList(externalTypes, stringList, classes.Count, classes); + AddStringsAndExternalTypesFromAttributes(stringList, externalTypes, classes.Count, assemblyAttributes); + + writer.Write(classes.Count); + writer.Write(externalTypes.Count); + foreach (IClass c in classes) { + writer.Write(c.FullyQualifiedName); + } + foreach (ClassNameTypeCountPair type in externalTypes) { + writer.Write(type.ClassName); + writer.Write(type.TypeParameterCount); + } + writer.Write(stringList.Count); + foreach (string text in stringList) { + writer.Write(text); + } + WriteAttributes(assemblyAttributes); + foreach (IClass c in classes) { + WriteClass(c); + // BinaryReader easily reads junk data when the file does not have the + // expected format, so we put a checking byte after each class. + writer.Write((byte)64); + } + } + + bool ReadClasses() + { + int classCount = reader.ReadInt32(); + int externalTypeCount = reader.ReadInt32(); + types = new IReturnType[classCount + externalTypeCount]; + DefaultClass[] classes = new DefaultClass[classCount]; + for (int i = 0; i < classes.Length; i++) { + DefaultClass c = new DefaultClass(pc.AssemblyCompilationUnit, reader.ReadString()); + classes[i] = c; + types[i] = c.DefaultReturnType; + } + for (int i = classCount; i < types.Length; i++) { + string name = reader.ReadString(); + types[i] = new GetClassReturnType(pc, name, reader.ReadByte()); + } + stringArray = new string[reader.ReadInt32()]; + for (int i = 0; i < stringArray.Length; i++) { + stringArray[i] = reader.ReadString(); + } + ReadAttributes(pc.AssemblyCompilationUnit); + for (int i = 0; i < classes.Length; i++) { + ReadClass(classes[i]); + pc.AddClassToNamespaceList(classes[i]); + if (reader.ReadByte() != 64) { + return false; + } + } + return true; + } + #endregion + + #region Write/Read Class + IClass currentClass; + + void WriteClass(IClass c) + { + this.currentClass = c; + WriteTemplates(c.TypeParameters); + writer.Write(c.BaseTypes.Count); + foreach (IReturnType type in c.BaseTypes) { + WriteType(type); + } + writer.Write((int)c.Modifiers); + if (c is DefaultClass) { + writer.Write(((DefaultClass)c).CalculatedFlags); + } else { + writer.Write((byte)0); + } + writer.Write((byte)c.ClassType); + WriteAttributes(c.Attributes); + writer.Write(c.InnerClasses.Count); + foreach (IClass innerClass in c.InnerClasses) { + writer.Write(innerClass.FullyQualifiedName); + WriteClass(innerClass); + } + this.currentClass = c; + writer.Write(c.Methods.Count); + foreach (IMethod method in c.Methods) { + WriteMethod(method); + } + writer.Write(c.Properties.Count); + foreach (IProperty property in c.Properties) { + WriteProperty(property); + } + writer.Write(c.Events.Count); + foreach (IEvent evt in c.Events) { + WriteEvent(evt); + } + writer.Write(c.Fields.Count); + foreach (IField field in c.Fields) { + WriteField(field); + } + this.currentClass = null; + } + + void WriteTemplates(IList list) + { + // read code exists twice: in ReadClass and ReadMethod + writer.Write((byte)list.Count); + foreach (ITypeParameter typeParameter in list) { + WriteString(typeParameter.Name); + } + foreach (ITypeParameter typeParameter in list) { + writer.Write(typeParameter.Constraints.Count); + foreach (IReturnType type in typeParameter.Constraints) { + WriteType(type); + } + } + } + + void ReadClass(DefaultClass c) + { + this.currentClass = c; + int count; + count = reader.ReadByte(); + for (int i = 0; i < count; i++) { + c.TypeParameters.Add(new DefaultTypeParameter(c, ReadString(), i)); + } + if (count > 0) { + foreach (ITypeParameter typeParameter in c.TypeParameters) { + count = reader.ReadInt32(); + for (int i = 0; i < count; i++) { + typeParameter.Constraints.Add(ReadType()); + } + } + } else { + c.TypeParameters = DefaultTypeParameter.EmptyTypeParameterList; + } + count = reader.ReadInt32(); + for (int i = 0; i < count; i++) { + c.BaseTypes.Add(ReadType()); + } + c.Modifiers = (ModifierEnum)reader.ReadInt32(); + c.CalculatedFlags = reader.ReadByte(); + c.ClassType = (ClassType)reader.ReadByte(); + ReadAttributes(c); + count = reader.ReadInt32(); + for (int i = 0; i < count; i++) { + DefaultClass innerClass = new DefaultClass(c.CompilationUnit, c); + innerClass.FullyQualifiedName = reader.ReadString(); + c.InnerClasses.Add(innerClass); + ReadClass(innerClass); + } + this.currentClass = c; + count = reader.ReadInt32(); + for (int i = 0; i < count; i++) { + c.Methods.Add(ReadMethod()); + } + count = reader.ReadInt32(); + for (int i = 0; i < count; i++) { + c.Properties.Add(ReadProperty()); + } + count = reader.ReadInt32(); + for (int i = 0; i < count; i++) { + c.Events.Add(ReadEvent()); + } + count = reader.ReadInt32(); + for (int i = 0; i < count; i++) { + c.Fields.Add(ReadField()); + } + this.currentClass = null; + } + #endregion + + #region Write/Read return types / Collect strings + /// + /// Finds all return types used in the class collection and adds the unknown ones + /// to the externalTypeIndices and externalTypes collections. + /// + void CreateExternalTypeList(List externalTypes, + List stringList, + int classCount, ICollection classes) + { + foreach (IClass c in classes) { + CreateExternalTypeList(externalTypes, stringList, classCount, c.InnerClasses); + AddStringsAndExternalTypesFromAttributes(stringList, externalTypes, classCount, c.Attributes); + foreach (IReturnType returnType in c.BaseTypes) { + AddExternalType(returnType, externalTypes, classCount); + } + foreach (ITypeParameter tp in c.TypeParameters) { + AddString(stringList, tp.Name); + foreach (IReturnType returnType in tp.Constraints) { + AddExternalType(returnType, externalTypes, classCount); + } + } + foreach (IField f in c.Fields) { + CreateExternalTypeListMember(externalTypes, stringList, classCount, f); + } + foreach (IEvent f in c.Events) { + CreateExternalTypeListMember(externalTypes, stringList, classCount, f); + } + foreach (IProperty p in c.Properties) { + CreateExternalTypeListMember(externalTypes, stringList, classCount, p); + foreach (IParameter parameter in p.Parameters) { + AddString(stringList, parameter.Name); + AddStringsAndExternalTypesFromAttributes(stringList, externalTypes, classCount, parameter.Attributes); + AddExternalType(parameter.ReturnType, externalTypes, classCount); + } + } + foreach (IMethod m in c.Methods) { + CreateExternalTypeListMember(externalTypes, stringList, classCount, m); + foreach (IParameter parameter in m.Parameters) { + AddString(stringList, parameter.Name); + AddStringsAndExternalTypesFromAttributes(stringList, externalTypes, classCount, parameter.Attributes); + AddExternalType(parameter.ReturnType, externalTypes, classCount); + } + foreach (ITypeParameter tp in m.TypeParameters) { + AddString(stringList, tp.Name); + foreach (IReturnType returnType in tp.Constraints) { + AddExternalType(returnType, externalTypes, classCount); + } + } + } + } + } + + void CreateExternalTypeListMember(List externalTypes, + List stringList, int classCount, + IMember member) + { + AddString(stringList, member.Name); + AddStringsAndExternalTypesFromAttributes(stringList, externalTypes, classCount, member.Attributes); + foreach (ExplicitInterfaceImplementation eii in member.InterfaceImplementations) { + AddString(stringList, eii.MemberName); + AddExternalType(eii.InterfaceReference, externalTypes, classCount); + } + AddExternalType(member.ReturnType, externalTypes, classCount); + } + + void AddString(List stringList, string text) + { + text = text ?? string.Empty; + if (!stringDict.ContainsKey(text)) { + stringDict.Add(text, stringList.Count); + stringList.Add(text); + } + } + + void AddExternalType(IReturnType rt, List externalTypes, int classCount) + { + if (rt.IsDefaultReturnType) { + ClassNameTypeCountPair pair = new ClassNameTypeCountPair(rt); + if (!classIndices.ContainsKey(pair)) { + classIndices.Add(pair, externalTypes.Count + classCount); + externalTypes.Add(pair); + } + } else if (rt.IsGenericReturnType) { + // ignore + } else if (rt.IsArrayReturnType) { + AddExternalType(rt.CastToArrayReturnType().ArrayElementType, externalTypes, classCount); + } else if (rt.IsConstructedReturnType) { + AddExternalType(rt.CastToConstructedReturnType().UnboundType, externalTypes, classCount); + foreach (IReturnType typeArgument in rt.CastToConstructedReturnType().TypeArguments) { + AddExternalType(typeArgument, externalTypes, classCount); + } + } else if (rt.IsDecoratingReturnType()) { + AddExternalType(rt.CastToDecoratingReturnType().BaseType, externalTypes, classCount); + } else { + LoggingService.Warn("Unknown return type: " + rt.ToString()); + } + } + + const int ArrayRTCode = -1; + const int ConstructedRTCode = -2; + const int TypeGenericRTCode = -3; + const int MethodGenericRTCode = -4; + const int NullRTReferenceCode = -5; + const int VoidRTCode = -6; + const int PointerRTCode = -7; + const int DynamicRTCode = -8; + + void WriteType(IReturnType rt) + { + if (rt == null) { + writer.Write(NullRTReferenceCode); + return; + } + if (rt.IsDefaultReturnType) { + string name = rt.FullyQualifiedName; + if (name == "System.Void") { + writer.Write(VoidRTCode); + } else if (name == "dynamic") { + writer.Write(DynamicRTCode); + } else { + writer.Write(classIndices[new ClassNameTypeCountPair(rt)]); + } + } else if (rt.IsGenericReturnType) { + GenericReturnType grt = rt.CastToGenericReturnType(); + if (grt.TypeParameter.Method != null) { + writer.Write(MethodGenericRTCode); + } else { + writer.Write(TypeGenericRTCode); + } + writer.Write(grt.TypeParameter.Index); + } else if (rt.IsArrayReturnType) { + writer.Write(ArrayRTCode); + writer.Write(rt.CastToArrayReturnType().ArrayDimensions); + WriteType(rt.CastToArrayReturnType().ArrayElementType); + } else if (rt.IsConstructedReturnType) { + ConstructedReturnType crt = rt.CastToConstructedReturnType(); + writer.Write(ConstructedRTCode); + WriteType(crt.UnboundType); + writer.Write((byte)crt.TypeArguments.Count); + foreach (IReturnType typeArgument in crt.TypeArguments) { + WriteType(typeArgument); + } + } else if (rt.IsDecoratingReturnType()) { + writer.Write(PointerRTCode); + WriteType(rt.CastToDecoratingReturnType().BaseType); + } else { + writer.Write(NullRTReferenceCode); + LoggingService.Warn("Unknown return type: " + rt.ToString()); + } + } + + // outerClass and outerMethod are required for generic return types + IReturnType ReadType() + { + int index = reader.ReadInt32(); + switch (index) { + case ArrayRTCode: + int dimensions = reader.ReadInt32(); + return new ArrayReturnType(pc, ReadType(), dimensions); + case ConstructedRTCode: + IReturnType baseType = ReadType(); + IReturnType[] typeArguments = new IReturnType[reader.ReadByte()]; + for (int i = 0; i < typeArguments.Length; i++) { + typeArguments[i] = ReadType(); + } + return new ConstructedReturnType(baseType, typeArguments); + case TypeGenericRTCode: + return new GenericReturnType(currentClass.TypeParameters[reader.ReadInt32()]); + case MethodGenericRTCode: + return new GenericReturnType(currentMethod.TypeParameters[reader.ReadInt32()]); + case NullRTReferenceCode: + return null; + case VoidRTCode: + return new VoidReturnType(pc); + case PointerRTCode: + return new PointerReturnType(ReadType()); + case DynamicRTCode: + return new DynamicReturnType(pc); + default: + return types[index]; + } + } + #endregion + + #region Write/Read class member + void WriteString(string text) + { + text = text ?? string.Empty; + writer.Write(stringDict[text]); + } + + string ReadString() + { + return stringArray[reader.ReadInt32()]; + } + + void WriteMember(IMember m) + { + WriteString(m.Name); + writer.Write((int)m.Modifiers); + WriteAttributes(m.Attributes); + writer.Write((ushort)m.InterfaceImplementations.Count); + foreach (ExplicitInterfaceImplementation iee in m.InterfaceImplementations) { + WriteType(iee.InterfaceReference); + WriteString(iee.MemberName); + } + if (!(m is IMethod)) { + // method must store ReturnType AFTER Template definitions + WriteType(m.ReturnType); + } + } + + void ReadMember(AbstractMember m) + { + // name is already read by the method that calls the member constructor + m.Modifiers = (ModifierEnum)reader.ReadInt32(); + ReadAttributes(m); + int interfaceImplCount = reader.ReadUInt16(); + for (int i = 0; i < interfaceImplCount; i++) { + m.InterfaceImplementations.Add(new ExplicitInterfaceImplementation(ReadType(), ReadString())); + } + if (!(m is IMethod)) { + m.ReturnType = ReadType(); + } + } + #endregion + + #region Write/Read attributes + void AddStringsAndExternalTypesFromAttributes(List stringList, + List externalTypes, int classCount, + IList attributes) + { + foreach (IAttribute a in attributes) { + AddExternalType(a.AttributeType, externalTypes, classCount); + foreach (object o in a.PositionalArguments) { + AddStringsAndExternalTypesFromAttributeArgument(stringList, attributes, externalTypes, classCount, o); + } + foreach (KeyValuePair pair in a.NamedArguments) { + AddString(stringList, pair.Key); + AddStringsAndExternalTypesFromAttributeArgument(stringList, attributes, externalTypes, classCount, pair.Value); + } + } + } + + void WriteAttributes(IList attributes) + { + writer.Write((ushort)attributes.Count); + foreach (IAttribute a in attributes) { + WriteType(a.AttributeType); + writer.Write((byte)a.AttributeTarget); + writer.Write((byte)a.PositionalArguments.Count); + foreach (object o in a.PositionalArguments) { + WriteAttributeArgument(o); + } + writer.Write((byte)a.NamedArguments.Count); + foreach (KeyValuePair pair in a.NamedArguments) { + WriteString(pair.Key); + WriteAttributeArgument(pair.Value); + } + } + } + + void ReadAttributes(ICompilationUnit cu) + { + int count = reader.ReadUInt16(); + ReadAttributes(cu.Attributes, count); + } + + void ReadAttributes(DefaultParameter parameter) + { + int count = reader.ReadUInt16(); + if (count > 0) { + ReadAttributes(parameter.Attributes, count); + } else { + parameter.Attributes = DefaultAttribute.EmptyAttributeList; + } + } + + void ReadAttributes(AbstractEntity decoration) + { + int count = reader.ReadUInt16(); + if (count > 0) { + ReadAttributes(decoration.Attributes, count); + } else { + decoration.Attributes = DefaultAttribute.EmptyAttributeList; + } + } + + void ReadAttributes(IList attributes, int count) + { + for (int i = 0; i < count; i++) { + IReturnType type = ReadType(); + DefaultAttribute attr = new DefaultAttribute(type, (AttributeTarget)reader.ReadByte()); + int posArgCount = reader.ReadByte(); + for (int j = 0; j < posArgCount; j++) { + attr.PositionalArguments.Add(ReadAttributeArgument()); + } + int namedArgCount = reader.ReadByte(); + for (int j = 0; j < namedArgCount; j++) { + attr.NamedArguments.Add(ReadString(), ReadAttributeArgument()); + } + attributes.Add(attr); + } + } + #endregion + + #region Write/Read attribute arguments + void AddStringsAndExternalTypesFromAttributeArgument(List stringList, IList attributes, + List externalTypes, int classCount, + object value) + { + if (value is string) { + AddString(stringList, (string)value); + } else if (value is IReturnType) { + AddExternalType((IReturnType)value, externalTypes, classCount); + } + } + + enum AttributeType : byte + { + Null, + String, + Type, + SByte, + Int16, + Int32, + Int64, + Byte, + UInt16, + UInt32, + UInt64, + Bool, + Single, + Double, + } + + void WriteAttributeArgument(object o) + { + if (o == null) { + writer.Write((byte)AttributeType.Null); + } else if (o is string) { + writer.Write((byte)AttributeType.String); + WriteString((string)o); + } else if (o is IReturnType) { + writer.Write((byte)AttributeType.Type); + WriteType((IReturnType)o); + } else if (o is Byte) { + writer.Write((byte)AttributeType.Byte); + writer.Write((Byte)o); + } else if (o is Int16) { + writer.Write((byte)AttributeType.Int16); + writer.Write((Int16)o); + } else if (o is Int32) { + writer.Write((byte)AttributeType.Int32); + writer.Write((Int32)o); + } else if (o is Int64) { + writer.Write((byte)AttributeType.Int64); + writer.Write((Int64)o); + } else if (o is SByte) { + writer.Write((byte)AttributeType.SByte); + writer.Write((SByte)o); + } else if (o is UInt16) { + writer.Write((byte)AttributeType.UInt16); + writer.Write((UInt16)o); + } else if (o is UInt32) { + writer.Write((byte)AttributeType.UInt32); + writer.Write((UInt32)o); + } else if (o is UInt64) { + writer.Write((byte)AttributeType.UInt64); + writer.Write((UInt64)o); + } else if (o is bool) { + writer.Write((byte)AttributeType.Bool); + writer.Write((bool)o); + } else if (o is Single) { + writer.Write((byte)AttributeType.Single); + writer.Write((Single)o); + } else if (o is Double) { + writer.Write((byte)AttributeType.Double); + writer.Write((Double)o); + } else { + writer.Write((byte)AttributeType.Null); + LoggingService.Warn("Cannot write attribute arguments of type " + o.GetType()); + } + } + + object ReadAttributeArgument() + { + byte type = reader.ReadByte(); + switch ((AttributeType)type) { + case AttributeType.Null: + return null; + case AttributeType.String: + return ReadString(); + case AttributeType.Type: + return ReadType(); + case AttributeType.SByte: + return reader.ReadSByte(); + case AttributeType.Int16: + return reader.ReadInt16(); + case AttributeType.Int32: + return reader.ReadInt32(); + case AttributeType.Int64: + return reader.ReadInt64(); + case AttributeType.Byte: + return reader.ReadByte(); + case AttributeType.UInt16: + return reader.ReadUInt16(); + case AttributeType.UInt32: + return reader.ReadUInt32(); + case AttributeType.UInt64: + return reader.ReadUInt64(); + case AttributeType.Bool: + return reader.ReadBoolean(); + case AttributeType.Single: + return reader.ReadSingle(); + case AttributeType.Double: + return reader.ReadDouble(); + default: + throw new NotSupportedException("Invalid attribute argument type code " + type); + } + } + #endregion + + #region Write/Read parameters + void WriteParameters(IList parameters) + { + writer.Write((ushort)parameters.Count); + foreach (IParameter p in parameters) { + WriteString(p.Name); + WriteType(p.ReturnType); + writer.Write((byte)p.Modifiers); + WriteAttributes(p.Attributes); + } + } + + void ReadParameters(DefaultMethod m) + { + int count = reader.ReadUInt16(); + if (count > 0) { + ReadParameters(m.Parameters, count); + } else { + m.Parameters = DefaultParameter.EmptyParameterList; + } + } + + void ReadParameters(DefaultProperty m) + { + int count = reader.ReadUInt16(); + if (count > 0) { + ReadParameters(m.Parameters, count); + } else { + m.Parameters = DefaultParameter.EmptyParameterList; + } + } + + void ReadParameters(IList parameters, int count) + { + for (int i = 0; i < count; i++) { + string name = ReadString(); + DefaultParameter p = new DefaultParameter(name, ReadType(), DomRegion.Empty); + p.Modifiers = (ParameterModifiers)reader.ReadByte(); + ReadAttributes(p); + parameters.Add(p); + } + } + #endregion + + #region Write/Read Method + IMethod currentMethod; + + void WriteMethod(IMethod m) + { + currentMethod = m; + WriteMember(m); + WriteTemplates(m.TypeParameters); + WriteType(m.ReturnType); + writer.Write(m.IsExtensionMethod); + WriteParameters(m.Parameters); + currentMethod = null; + } + + IMethod ReadMethod() + { + DefaultMethod m = new DefaultMethod(currentClass, ReadString()); + currentMethod = m; + ReadMember(m); + int count = reader.ReadByte(); + for (int i = 0; i < count; i++) { + m.TypeParameters.Add(new DefaultTypeParameter(m, ReadString(), i)); + } + if (count > 0) { + foreach (ITypeParameter typeParameter in m.TypeParameters) { + count = reader.ReadInt32(); + for (int i = 0; i < count; i++) { + typeParameter.Constraints.Add(ReadType()); + } + } + } else { + m.TypeParameters = DefaultTypeParameter.EmptyTypeParameterList; + } + m.ReturnType = ReadType(); + m.IsExtensionMethod = reader.ReadBoolean(); + ReadParameters(m); + currentMethod = null; + return m; + } + #endregion + + #region Write/Read Property + void WriteProperty(IProperty p) + { + WriteMember(p); + DefaultProperty dp = p as DefaultProperty; + if (dp != null) { + writer.Write(dp.accessFlags); + } else { + writer.Write((byte)0); + } + writer.Write((byte)((p.GetterModifiers != ModifierEnum.None ? 1 : 0) + (p.SetterModifiers != ModifierEnum.None ? 2 : 0))); + if (p.GetterModifiers != ModifierEnum.None) { + writer.Write((int)p.GetterModifiers); + } + if (p.SetterModifiers != ModifierEnum.None) { + writer.Write((int)p.SetterModifiers); + } + WriteParameters(p.Parameters); + } + + IProperty ReadProperty() + { + DefaultProperty p = new DefaultProperty(currentClass, ReadString()); + ReadMember(p); + p.accessFlags = reader.ReadByte(); + byte b = reader.ReadByte(); + if ((b & 1) == 1) { + p.GetterModifiers = (ModifierEnum)reader.ReadInt32(); + } + if ((b & 2) == 2) { + p.SetterModifiers = (ModifierEnum)reader.ReadInt32(); + } + ReadParameters(p); + return p; + } + #endregion + + #region Write/Read Event + void WriteEvent(IEvent p) + { + WriteMember(p); + } + + IEvent ReadEvent() + { + DefaultEvent p = new DefaultEvent(currentClass, ReadString()); + ReadMember(p); + return p; + } + #endregion + + #region Write/Read Field + void WriteField(IField p) + { + WriteMember(p); + } + + IField ReadField() + { + DefaultField p = new DefaultField(currentClass, ReadString()); + ReadMember(p); + return p; + } + #endregion + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs new file mode 100644 index 000000000..624b69c70 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs @@ -0,0 +1,232 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace ICSharpCode.SharpDevelop.Dom.ReflectionLayer +{ + public class ReflectionClass : DefaultClass + { + const BindingFlags flags = BindingFlags.Instance | + BindingFlags.Static | + BindingFlags.NonPublic | + BindingFlags.DeclaredOnly | + BindingFlags.Public; + + void InitMembers(Type type) + { + foreach (Type nestedType in type.GetNestedTypes(flags)) { + // We cannot use nestedType.IsVisible - that only checks for public types, + // but we also need to load protected types. + if (nestedType.IsNestedPublic || nestedType.IsNestedFamily || nestedType.IsNestedFamORAssem) { + string name = this.FullyQualifiedName + "." + nestedType.Name; + InnerClasses.Add(new ReflectionClass(CompilationUnit, nestedType, name, this)); + } + } + + foreach (FieldInfo field in type.GetFields(flags)) { + if (!field.IsPublic && !field.IsFamily && !field.IsFamilyOrAssembly) continue; + if (!field.IsSpecialName) { + Fields.Add(new ReflectionField(field, this)); + } + } + + foreach (PropertyInfo propertyInfo in type.GetProperties(flags)) { + ReflectionProperty prop = new ReflectionProperty(propertyInfo, this); + if (prop.IsPublic || prop.IsProtected) + Properties.Add(prop); + } + + foreach (ConstructorInfo constructorInfo in type.GetConstructors(flags)) { + if (!constructorInfo.IsPublic && !constructorInfo.IsFamily && !constructorInfo.IsFamilyOrAssembly) continue; + Methods.Add(new ReflectionMethod(constructorInfo, this)); + } + + foreach (MethodInfo methodInfo in type.GetMethods(flags)) { + if (!methodInfo.IsPublic && !methodInfo.IsFamily && !methodInfo.IsFamilyOrAssembly) continue; + if (!methodInfo.IsSpecialName) { + Methods.Add(new ReflectionMethod(methodInfo, this)); + } + } + this.AddDefaultConstructorIfRequired = (this.ClassType == ClassType.Struct || this.ClassType == ClassType.Enum); + + foreach (EventInfo eventInfo in type.GetEvents(flags)) { + Events.Add(new ReflectionEvent(eventInfo, this)); + } + } + + static bool IsDelegate(Type type) + { + return type.IsSubclassOf(typeof(Delegate)) && type != typeof(MulticastDelegate); + } + + internal static void AddAttributes(IProjectContent pc, IList list, IList attributes) + { + foreach (CustomAttributeData att in attributes) { + DefaultAttribute a = new DefaultAttribute(ReflectionReturnType.Create(pc, att.Constructor.DeclaringType)); + foreach (CustomAttributeTypedArgument arg in att.ConstructorArguments) { + a.PositionalArguments.Add(ReplaceTypeByIReturnType(pc, arg.Value)); + } + foreach (CustomAttributeNamedArgument arg in att.NamedArguments) { + a.NamedArguments.Add(arg.MemberInfo.Name, ReplaceTypeByIReturnType(pc, arg.TypedValue.Value)); + } + list.Add(a); + } + } + + static object ReplaceTypeByIReturnType(IProjectContent pc, object val) + { + if (val is Type) { + return ReflectionReturnType.Create(pc, (Type)val, forceGenericType: false); + } else { + return val; + } + } + + internal static void ApplySpecialsFromAttributes(DefaultClass c) + { + foreach (IAttribute att in c.Attributes) { + if (att.AttributeType.FullyQualifiedName == "Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute" + || att.AttributeType.FullyQualifiedName == "System.Runtime.CompilerServices.CompilerGlobalScopeAttribute") + { + c.ClassType = ClassType.Module; + break; + } + } + } + + public static string SplitTypeParameterCountFromReflectionName(string reflectionName) + { + int lastBackTick = reflectionName.LastIndexOf('`'); + if (lastBackTick < 0) + return reflectionName; + else + return reflectionName.Substring(0, lastBackTick); + } + + 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; + } + } + + public static string ConvertReflectionNameToFullName(string reflectionName, out int typeParameterCount) + { + if (reflectionName.IndexOf('+') > 0) { + typeParameterCount = 0; + StringBuilder newName = new StringBuilder(); + foreach (string namepart in reflectionName.Split('+')) { + if (newName.Length > 0) + newName.Append('.'); + int partTypeParameterCount; + newName.Append(SplitTypeParameterCountFromReflectionName(namepart, out partTypeParameterCount)); + typeParameterCount += partTypeParameterCount; + } + return newName.ToString(); + } else { + return SplitTypeParameterCountFromReflectionName(reflectionName, out typeParameterCount); + } + } + + public ReflectionClass(ICompilationUnit compilationUnit, Type type, string fullName, IClass declaringType) : base(compilationUnit, declaringType) + { + FullyQualifiedName = SplitTypeParameterCountFromReflectionName(fullName); + + try { + AddAttributes(compilationUnit.ProjectContent, this.Attributes, CustomAttributeData.GetCustomAttributes(type)); + } catch (Exception ex) { + HostCallback.ShowError("Error reading custom attributes", ex); + } + + // set classtype + if (type.IsInterface) { + this.ClassType = ClassType.Interface; + } else if (type.IsEnum) { + this.ClassType = ClassType.Enum; + } else if (type.IsValueType) { + this.ClassType = ClassType.Struct; + } else if (IsDelegate(type)) { + this.ClassType = ClassType.Delegate; + } else { + this.ClassType = ClassType.Class; + ApplySpecialsFromAttributes(this); + } + if (type.IsGenericTypeDefinition) { + foreach (Type g in type.GetGenericArguments()) { + this.TypeParameters.Add(new DefaultTypeParameter(this, g)); + } + int i = 0; + foreach (Type g in type.GetGenericArguments()) { + AddConstraintsFromType(this.TypeParameters[i++], g); + } + } + + ModifierEnum modifiers = ModifierEnum.None; + + if (type.IsNestedAssembly) { + modifiers |= ModifierEnum.Internal; + } + if (type.IsSealed) { + modifiers |= ModifierEnum.Sealed; + } + if (type.IsAbstract) { + modifiers |= ModifierEnum.Abstract; + } + if (type.IsSealed && type.IsAbstract) { + modifiers |= ModifierEnum.Static; + } + + if (type.IsNestedPrivate ) { // I assume that private is used most and public last (at least should be) + modifiers |= ModifierEnum.Private; + } else if (type.IsNestedFamily ) { + modifiers |= ModifierEnum.Protected; + } else if (type.IsNestedPublic || type.IsPublic) { + modifiers |= ModifierEnum.Public; + } else if (type.IsNotPublic) { + modifiers |= ModifierEnum.Internal; + } else if (type.IsNestedFamORAssem || type.IsNestedFamANDAssem) { + modifiers |= ModifierEnum.Protected; + modifiers |= ModifierEnum.Internal; + } + this.Modifiers = modifiers; + + // set base classes + if (type.BaseType != null) { // it's null for System.Object ONLY !!! + BaseTypes.Add(ReflectionReturnType.Create(this, type.BaseType)); + } + + foreach (Type iface in type.GetInterfaces()) { + BaseTypes.Add(ReflectionReturnType.Create(this, iface)); + } + + InitMembers(type); + } + + internal static void AddConstraintsFromType(ITypeParameter tp, Type type) + { + foreach (Type constraint in type.GetGenericParameterConstraints()) { + if (tp.Method != null) { + tp.Constraints.Add(ReflectionReturnType.Create(tp.Method, constraint)); + } else { + tp.Constraints.Add(ReflectionReturnType.Create(tp.Class, constraint)); + } + } + } + + protected override bool KeepInheritanceTree { + get { return true; } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionEvent.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionEvent.cs new file mode 100644 index 000000000..0dad84583 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionEvent.cs @@ -0,0 +1,62 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Reflection; + +namespace ICSharpCode.SharpDevelop.Dom.ReflectionLayer +{ + internal class ReflectionEvent : DefaultEvent + { + public ReflectionEvent(EventInfo eventInfo, IClass declaringType) : base(declaringType, eventInfo.Name) + { + this.ReturnType = ReflectionReturnType.Create(this, eventInfo.EventHandlerType, attributeProvider: eventInfo); + + // get modifiers + MethodInfo methodBase = null; + try { + methodBase = eventInfo.GetAddMethod(true); + } catch (Exception) {} + + if (methodBase == null) { + try { + methodBase = eventInfo.GetRemoveMethod(true); + } catch (Exception) {} + } + + ModifierEnum modifiers = ModifierEnum.None; + if (methodBase != null) { + if (methodBase.IsStatic) { + modifiers |= ModifierEnum.Static; + } + + if (methodBase.IsAssembly) { + modifiers |= ModifierEnum.Internal; + } + + if (methodBase.IsPrivate) { // I assume that private is used most and public last (at least should be) + modifiers |= ModifierEnum.Private; + } else if (methodBase.IsFamily || methodBase.IsFamilyOrAssembly) { + modifiers |= ModifierEnum.Protected; + } else if (methodBase.IsPublic) { + modifiers |= ModifierEnum.Public; + } else { + modifiers |= ModifierEnum.Internal; + } + + if (methodBase.IsFinal) { + modifiers |= ModifierEnum.Sealed; + } else if (methodBase.IsAbstract) { + modifiers |= ModifierEnum.Abstract; + } else if (methodBase.IsVirtual) { + modifiers |= ModifierEnum.Virtual; + } + } else { + // assume public property, if no methodBase could be get. + modifiers = ModifierEnum.Public; + } + this.Modifiers = modifiers; + + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionField.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionField.cs new file mode 100644 index 000000000..81005996a --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionField.cs @@ -0,0 +1,44 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Reflection; + +namespace ICSharpCode.SharpDevelop.Dom.ReflectionLayer +{ + internal class ReflectionField : DefaultField + { + public ReflectionField(FieldInfo fieldInfo, IClass declaringType) : base(declaringType, fieldInfo.Name) + { + this.ReturnType = ReflectionReturnType.Create(this, fieldInfo.FieldType, attributeProvider: fieldInfo); + + ModifierEnum modifiers = ModifierEnum.None; + if (fieldInfo.IsInitOnly) { + modifiers |= ModifierEnum.Readonly; + } + + if (fieldInfo.IsStatic) { + modifiers |= ModifierEnum.Static; + } + + if (fieldInfo.IsAssembly) { + modifiers |= ModifierEnum.Internal; + } + + if (fieldInfo.IsPrivate) { // I assume that private is used most and public last (at least should be) + modifiers |= ModifierEnum.Private; + } else if (fieldInfo.IsFamily || fieldInfo.IsFamilyOrAssembly) { + modifiers |= ModifierEnum.Protected; + } else if (fieldInfo.IsPublic) { + modifiers |= ModifierEnum.Public; + } else { + modifiers |= ModifierEnum.Internal; + } + + if (fieldInfo.IsLiteral) { + modifiers |= ModifierEnum.Const; + } + this.Modifiers = modifiers; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionMethod.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionMethod.cs new file mode 100644 index 000000000..508b1b17b --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionMethod.cs @@ -0,0 +1,80 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Reflection; + +namespace ICSharpCode.SharpDevelop.Dom.ReflectionLayer +{ + internal class ReflectionMethod : DefaultMethod + { + internal static void ApplySpecialsFromAttributes(DefaultMethod m) + { + if (m.IsStatic) { + foreach (IAttribute a in m.Attributes) { + string attributeName = a.AttributeType.FullyQualifiedName; + if (attributeName == "System.Runtime.CompilerServices.ExtensionAttribute" + || attributeName == "Boo.Lang.ExtensionAttribute") + { + m.IsExtensionMethod = true; + } + } + } + } + + public ReflectionMethod(MethodBase methodBase, ReflectionClass declaringType) + : base(declaringType, methodBase is ConstructorInfo ? "#ctor" : methodBase.Name) + { + if (methodBase is MethodInfo) { + MethodInfo m = ((MethodInfo)methodBase); + this.ReturnType = ReflectionReturnType.Create(this, m.ReturnType, attributeProvider: m.ReturnTypeCustomAttributes); + } else if (methodBase is ConstructorInfo) { + this.ReturnType = DeclaringType.DefaultReturnType; + } + + foreach (ParameterInfo paramInfo in methodBase.GetParameters()) { + this.Parameters.Add(new ReflectionParameter(paramInfo, this)); + } + + if (methodBase.IsGenericMethodDefinition) { + foreach (Type g in methodBase.GetGenericArguments()) { + this.TypeParameters.Add(new DefaultTypeParameter(this, g)); + } + int i = 0; + foreach (Type g in methodBase.GetGenericArguments()) { + ReflectionClass.AddConstraintsFromType(this.TypeParameters[i++], g); + } + } + + ModifierEnum modifiers = ModifierEnum.None; + if (methodBase.IsStatic) { + modifiers |= ModifierEnum.Static; + } + if (methodBase.IsPrivate) { // I assume that private is used most and public last (at least should be) + modifiers |= ModifierEnum.Private; + } else if (methodBase.IsFamily || methodBase.IsFamilyOrAssembly) { + modifiers |= ModifierEnum.Protected; + } else if (methodBase.IsPublic) { + modifiers |= ModifierEnum.Public; + } else { + modifiers |= ModifierEnum.Internal; + } + + if (methodBase.IsFinal) { + modifiers |= ModifierEnum.Sealed; + } else if (methodBase.IsAbstract) { + modifiers |= ModifierEnum.Abstract; + } else if (methodBase.IsVirtual) { + if ((methodBase.Attributes & MethodAttributes.NewSlot) != 0) + modifiers |= ModifierEnum.Virtual; + else + modifiers |= ModifierEnum.Override; + } + + this.Modifiers = modifiers; + + ReflectionClass.AddAttributes(declaringType.ProjectContent, this.Attributes, CustomAttributeData.GetCustomAttributes(methodBase)); + ApplySpecialsFromAttributes(this); + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionParameter.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionParameter.cs new file mode 100644 index 000000000..04499d072 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionParameter.cs @@ -0,0 +1,36 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Reflection; + +namespace ICSharpCode.SharpDevelop.Dom.ReflectionLayer +{ + public class ReflectionParameter : DefaultParameter + { + public ReflectionParameter(ParameterInfo parameterInfo, IMember member) : base(parameterInfo.Name) + { + Type type = parameterInfo.ParameterType; + + this.ReturnType = ReflectionReturnType.Create(member, type, attributeProvider: parameterInfo); + + if (type.IsByRef && parameterInfo.IsOut) { + this.Modifiers = ParameterModifiers.Out; + } else if (type.IsByRef) { + this.Modifiers = ParameterModifiers.Ref; + } + + if (parameterInfo.IsOptional) { + this.Modifiers |= ParameterModifiers.Optional; + } + if (type.IsArray && type != typeof(Array)) { + foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(parameterInfo)) { + if (data.Constructor.DeclaringType.FullName == typeof(ParamArrayAttribute).FullName) { + this.Modifiers |= ParameterModifiers.Params; + break; + } + } + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionProperty.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionProperty.cs new file mode 100644 index 000000000..b5bf86ff0 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionProperty.cs @@ -0,0 +1,107 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Reflection; + +namespace ICSharpCode.SharpDevelop.Dom.ReflectionLayer +{ + internal class ReflectionProperty : DefaultProperty + { + public ReflectionProperty(PropertyInfo propertyInfo, IClass declaringType) : base(declaringType, propertyInfo.Name) + { + this.ReturnType = ReflectionReturnType.Create(this, propertyInfo.PropertyType, attributeProvider: propertyInfo); + + CanGet = propertyInfo.CanRead; + CanSet = propertyInfo.CanWrite; + + ParameterInfo[] parameterInfo = propertyInfo.GetIndexParameters(); + if (parameterInfo != null && parameterInfo.Length > 0) { + // check if this property is an indexer (=default member of parent class) + foreach (MemberInfo memberInfo in propertyInfo.DeclaringType.GetDefaultMembers()) { + if (memberInfo == propertyInfo) { + this.IsIndexer = true; + break; + } + } + // there are only few properties with parameters, so we can load them immediately + foreach (ParameterInfo info in parameterInfo) { + this.Parameters.Add(new ReflectionParameter(info, this)); + } + } + + MethodInfo getterMethod = null; + try { + getterMethod = propertyInfo.GetGetMethod(true); + } catch (Exception) {} + + MethodInfo setterMethod = null; + try { + setterMethod = propertyInfo.GetSetMethod(true); + } catch (Exception) {} + + MethodInfo methodBase = getterMethod ?? setterMethod; + + ModifierEnum modifiers = ModifierEnum.None; + if (methodBase != null) { + if (methodBase.IsStatic) { + modifiers |= ModifierEnum.Static; + } + + if (methodBase.IsAssembly) { + modifiers |= ModifierEnum.Internal; + } + + if (methodBase.IsPrivate) { // I assume that private is used most and public last (at least should be) + modifiers |= ModifierEnum.Private; + } else if (methodBase.IsFamily || methodBase.IsFamilyOrAssembly) { + modifiers |= ModifierEnum.Protected; + } else if (methodBase.IsPublic) { + modifiers |= ModifierEnum.Public; + } else { + modifiers |= ModifierEnum.Internal; + } + + if (methodBase.IsFinal) { + modifiers |= ModifierEnum.Sealed; + } else if (methodBase.IsAbstract) { + modifiers |= ModifierEnum.Abstract; + } else if (methodBase.IsVirtual) { + modifiers |= ModifierEnum.Virtual; + } + } else { // assume public property, if no methodBase could be get. + modifiers = ModifierEnum.Public; + } + this.Modifiers = modifiers; + if (getterMethod != null) { + ModifierEnum getterModifier = GetAccessorModifier(getterMethod); + if (getterModifier == ModifierEnum.Private) { + this.CanGet = false; + } else { + if (getterModifier != (modifiers & ModifierEnum.VisibilityMask)) + this.GetterModifiers = getterModifier; + } + } + if (setterMethod != null) { + ModifierEnum setterModifier = GetAccessorModifier(setterMethod); + if (setterModifier == ModifierEnum.Private) { + this.CanSet = false; + } else { + if (setterModifier != (modifiers & ModifierEnum.VisibilityMask)) + this.SetterModifiers = setterModifier; + } + } + } + + static ModifierEnum GetAccessorModifier(MethodInfo accessor) + { + if (accessor.IsPublic) { + return ModifierEnum.Public; + } else if (accessor.IsFamily || accessor.IsFamilyOrAssembly) { + return ModifierEnum.Protected; + } else { + return ModifierEnum.Private; // or internal, we don't care about that difference + } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionReturnType.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionReturnType.cs new file mode 100644 index 000000000..3549d0f5b --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionReturnType.cs @@ -0,0 +1,209 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; + +namespace ICSharpCode.SharpDevelop.Dom.ReflectionLayer +{ + public static class ReflectionReturnType + { + public static bool IsDefaultType(Type type) + { + return !type.IsArray && !type.IsGenericType && !type.IsGenericParameter; + } + + #region Parse Reflection Type Name + public static IReturnType Parse(IProjectContent pc, string reflectionTypeName) + { + if (pc == null) + throw new ArgumentNullException("pc"); + using (var tokenizer = Tokenize(reflectionTypeName)) { + tokenizer.MoveNext(); + IReturnType result = Parse(pc, tokenizer); + if (tokenizer.Current != null) + throw new ReflectionTypeNameSyntaxError("Expected end of type name, but found " + tokenizer.Current); + return result; + } + } + + static IReturnType Parse(IProjectContent pc, IEnumerator tokenizer) + { + string typeName = tokenizer.Current; + if (typeName == null) + throw new ReflectionTypeNameSyntaxError("Unexpected end of type name"); + tokenizer.MoveNext(); + int typeParameterCount; + typeName = ReflectionClass.SplitTypeParameterCountFromReflectionName(typeName, out typeParameterCount); + IReturnType rt = new GetClassReturnType(pc, typeName, typeParameterCount); + if (tokenizer.Current == "[") { + // this is a constructed type + List typeArguments = new List(); + do { + tokenizer.MoveNext(); + if (tokenizer.Current != "[") + throw new ReflectionTypeNameSyntaxError("Expected '['"); + tokenizer.MoveNext(); + typeArguments.Add(Parse(pc, tokenizer)); + if (tokenizer.Current != "]") + throw new ReflectionTypeNameSyntaxError("Expected ']' after generic argument"); + tokenizer.MoveNext(); + } while (tokenizer.Current == ","); + if (tokenizer.Current != "]") + throw new ReflectionTypeNameSyntaxError("Expected ']' after generic argument list"); + tokenizer.MoveNext(); + + rt = new ConstructedReturnType(rt, typeArguments); + } + while (tokenizer.Current == ",") { + tokenizer.MoveNext(); + string token = tokenizer.Current; + if (token != null && token != "," && token != "[" && token != "]") + tokenizer.MoveNext(); + } + return rt; + } + + static IEnumerator Tokenize(string reflectionTypeName) + { + StringBuilder currentText = new StringBuilder(); + for (int i = 0; i < reflectionTypeName.Length; i++) { + char c = reflectionTypeName[i]; + if (c == ',' || c == '[' || c == ']') { + if (currentText.Length > 0) { + yield return currentText.ToString(); + currentText.Length = 0; + } + yield return c.ToString(); + } else { + currentText.Append(c); + } + } + if (currentText.Length > 0) + yield return currentText.ToString(); + yield return null; + } + #endregion + + /// + /// Creates a IReturnType from the reflection type. + /// + /// The project content used as context. + /// The member used as context (e.g. as GenericReturnType) + /// The reflection return type that should be converted + /// Set this parameter to false to create a direct return type + /// (without GetClassReturnType indirection) where possible + /// Set this parameter to false to allow unbound generic types + /// The IReturnType + /// Attribute provider for lookup of [Dynamic] attribute + public static IReturnType Create(IProjectContent pc, Type type, + IEntity entity = null, + bool createLazyReturnType = false, + bool forceGenericType = true, + ICustomAttributeProvider attributeProvider = null) + { + if (pc == null) + throw new ArgumentNullException("pc"); + if (type == null) + throw new ArgumentNullException("type"); + int typeIndex = 0; + return Create(pc, type, entity, createLazyReturnType, attributeProvider, ref typeIndex, forceGenericType); + } + + public static IReturnType Create(IEntity entity, Type type, + bool createLazyReturnType = false, + bool forceGenericType = true, + ICustomAttributeProvider attributeProvider = null) + { + if (entity == null) + throw new ArgumentNullException("entity"); + if (type == null) + throw new ArgumentNullException("type"); + int typeIndex = 0; + return Create(entity.ProjectContent, type, entity, createLazyReturnType, attributeProvider, ref typeIndex, forceGenericType); + } + + static IReturnType Create(IProjectContent pc, Type type, + IEntity member, + bool createLazyReturnType, + ICustomAttributeProvider attributeProvider, + ref int typeIndex, + bool forceGenericType = true) + { + if (type.IsByRef) { + // TODO: Use ByRefRefReturnType + return Create(pc, type.GetElementType(), member, createLazyReturnType, attributeProvider, ref typeIndex); + } else if (type.IsPointer) { + typeIndex++; + return new PointerReturnType(Create(pc, type.GetElementType(), member, createLazyReturnType, attributeProvider, ref typeIndex)); + } else if (type.IsArray) { + typeIndex++; + return new ArrayReturnType(pc, Create(pc, type.GetElementType(), member, createLazyReturnType, attributeProvider, ref typeIndex), type.GetArrayRank()); + } else if (type.IsGenericType && (forceGenericType || !type.IsGenericTypeDefinition)) { + IReturnType baseType = Create(pc, type.GetGenericTypeDefinition(), member, createLazyReturnType, attributeProvider, ref typeIndex, forceGenericType: false); + Type[] args = type.GetGenericArguments(); + List para = new List(args.Length); + for (int i = 0; i < args.Length; ++i) { + typeIndex++; + para.Add(Create(pc, args[i], member, createLazyReturnType, attributeProvider, ref typeIndex)); + } + return new ConstructedReturnType(baseType, para); + } else if (type.IsGenericParameter) { + IClass c = (member is IClass) ? (IClass)member : (member is IMember) ? ((IMember)member).DeclaringType : null; + if (c != null && type.GenericParameterPosition < c.TypeParameters.Count) { + if (c.TypeParameters[type.GenericParameterPosition].Name == type.Name) { + return new GenericReturnType(c.TypeParameters[type.GenericParameterPosition]); + } + } + if (type.DeclaringMethod != null) { + IMethod method = member as IMethod; + if (method != null) { + if (type.GenericParameterPosition < method.TypeParameters.Count) { + return new GenericReturnType(method.TypeParameters[type.GenericParameterPosition]); + } + return new GenericReturnType(new DefaultTypeParameter(method, type)); + } + } + return new GenericReturnType(new DefaultTypeParameter(c, type)); + } else { + string name = type.FullName; + if (name == null) + throw new ApplicationException("type.FullName returned null. Type: " + type.ToString()); + int typeParameterCount; + name = ReflectionClass.ConvertReflectionNameToFullName(name, out typeParameterCount); + + if (typeParameterCount == 0 && name == "System.Object" && HasDynamicAttribute(attributeProvider, typeIndex)) + return new DynamicReturnType(pc); + + if (!createLazyReturnType) { + IClass c = pc.GetClass(name, typeParameterCount); + if (c != null) + return c.DefaultReturnType; + // example where name is not found: pointers like System.Char* + // or when the class is in a assembly that is not referenced + } + return new GetClassReturnType(pc, name, typeParameterCount); + } + } + + static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex) + { + if (attributeProvider == null) + return false; + object[] attributes = attributeProvider.GetCustomAttributes(typeof(DynamicAttribute), false); + if (attributes.Length == 0) + return false; + DynamicAttribute attr = attributes[0] as DynamicAttribute; + if (attr != null) { + var transformFlags = attr.TransformFlags; + if (transformFlags != null && typeIndex < transformFlags.Count) + return transformFlags[typeIndex]; + return true; + } + return false; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionTypeNameSyntaxError.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionTypeNameSyntaxError.cs new file mode 100644 index 000000000..5fd408c88 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionTypeNameSyntaxError.cs @@ -0,0 +1,31 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpDevelop.Dom.ReflectionLayer +{ + /// + /// Thrown if there is a syntax error in a type name. + /// + [Serializable] + public class ReflectionTypeNameSyntaxError : Exception + { + public ReflectionTypeNameSyntaxError() : base() + { + } + + public ReflectionTypeNameSyntaxError(string message) : base(message) + { + } + + public ReflectionTypeNameSyntaxError(string message, Exception innerException) : base(message, innerException) + { + } + + protected ReflectionTypeNameSyntaxError(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ResolveResult.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ResolveResult.cs new file mode 100644 index 000000000..fd4c7851b --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/ResolveResult.cs @@ -0,0 +1,1060 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.Ast; + +namespace ICSharpCode.SharpDevelop.Dom +{ + #region ResolveResult + /// + /// The base class of all resolve results. + /// This class is used whenever an expression is not one of the special expressions + /// (having their own ResolveResult class). + /// The ResolveResult specified the location where Resolve was called (Class+Member) + /// and the type of the resolved expression. + /// + public class ResolveResult : AbstractFreezable, ICloneable + { + IClass callingClass; + IMember callingMember; + IReturnType resolvedType; + + public ResolveResult(IClass callingClass, IMember callingMember, IReturnType resolvedType) + { + this.callingClass = callingClass; + this.callingMember = callingMember; + this.resolvedType = resolvedType; + } + + public virtual bool IsValid { + get { return true; } + } + + /// + /// Gets the class that contained the expression used to get this ResolveResult. + /// Can be null when the class is unknown. + /// + public IClass CallingClass { + get { return callingClass; } + } + + /// + /// Gets the member (method or property in ) that contained the + /// expression used to get this ResolveResult. + /// Can be null when the expression was not inside a member or the member is unknown. + /// + public IMember CallingMember { + get { return callingMember; } + } + + /// + /// Gets the type of the resolved expression. + /// Can be null when the type cannot be represented by a IReturnType (e.g. when the + /// expression was a namespace name). + /// + public IReturnType ResolvedType { + get { return resolvedType; } + set { + CheckBeforeMutation(); + resolvedType = value; + } + } + + public virtual ResolveResult Clone() + { + return new ResolveResult(callingClass, callingMember, resolvedType); + } + + object ICloneable.Clone() + { + return this.Clone(); + } + + bool showAllNamespacesContentsInCC = false; + /// + /// Gets code completion data for this ResolveResult. + /// + /// + /// If true, items (e.g. extension methods) from all namespaces are returned, regardless current imports. Default is false. + /// + public List GetCompletionData(IProjectContent projectContent, bool showItemsFromAllNamespaces) + { + // Little hack - store value in a property to pass it to GetCompletionData(LanguageProperties language, bool showStatic) + // Otherwise we would have to add it as a parameter to GetCompletionData(IProjectContent projectContent), + // which would change signature in classes overriding this method as well. + this.showAllNamespacesContentsInCC = showItemsFromAllNamespaces; + var result = GetCompletionData(projectContent); + this.showAllNamespacesContentsInCC = false; + return result; + } + + public virtual List GetCompletionData(IProjectContent projectContent) + { + return GetCompletionData(projectContent.Language, false); + } + + protected List GetCompletionData(LanguageProperties language, bool showStatic) + { + if (resolvedType == null) return null; + List res = new List(); + + foreach (IMember m in MemberLookupHelper.GetAccessibleMembers(resolvedType, callingClass, language)) { + if (language.ShowMember(m, showStatic)) + res.Add(m); + } + + if (!showStatic && callingClass != null) { + AddExtensions(language, res.Add, callingClass, resolvedType, this.showAllNamespacesContentsInCC); + } + + return res; + } + + /// + /// Adds extension methods to . + /// + public static void AddExtensions(LanguageProperties language, Action methodFound, IClass callingClass, IReturnType resolvedType, bool searchInAllNamespaces = false) + { + if (language == null) + throw new ArgumentNullException("language"); + if (methodFound == null) + throw new ArgumentNullException("methodFound"); + if (resolvedType == null) + throw new ArgumentNullException("resolvedType"); + if (callingClass == null) + throw new ArgumentNullException("callingClass"); + + // convert resolvedType into direct type to speed up the IsApplicable lookups + resolvedType = resolvedType.GetDirectReturnType(); + + foreach (IMethodOrProperty mp in CtrlSpaceResolveHelper.FindAllExtensions(language, callingClass, searchInAllNamespaces)) { + TryAddExtension(language, methodFound, mp, resolvedType); + } + } + + static void TryAddExtension(LanguageProperties language, Action methodFound, IMethodOrProperty ext, IReturnType resolvedType) + { + // now add the extension method if it fits the type + if (MemberLookupHelper.IsApplicable(resolvedType, ext.Parameters[0].ReturnType, ext as IMethod)) { + IMethod method = ext as IMethod; + if (method != null && method.TypeParameters.Count > 0) { + IReturnType[] typeArguments = new IReturnType[method.TypeParameters.Count]; + MemberLookupHelper.InferTypeArgument(method.Parameters[0].ReturnType, resolvedType, typeArguments); + for (int i = 0; i < typeArguments.Length; i++) { + if (typeArguments[i] != null) { + ext = (IMethod)ext.CreateSpecializedMember(); + ext.ReturnType = ConstructedReturnType.TranslateType(ext.ReturnType, typeArguments, true); + for (int j = 0; j < ext.Parameters.Count; ++j) { + ext.Parameters[j].ReturnType = ConstructedReturnType.TranslateType(ext.Parameters[j].ReturnType, typeArguments, true); + } + break; + } + } + } + methodFound(ext); + } + } + + public virtual FilePosition GetDefinitionPosition() + { + // this is only possible on some subclasses of ResolveResult + return FilePosition.Empty; + } + + /// + /// Gets if this ResolveResult represents a reference to the specified entity. + /// + public virtual bool IsReferenceTo(IEntity entity) + { + return false; + } + } + #endregion + + #region MixedResolveResult + /// + /// The MixedResolveResult is used when an expression can have multiple meanings, for example + /// "Size" in a class deriving from "Control". + /// + public class MixedResolveResult : ResolveResult + { + ResolveResult primaryResult, secondaryResult; + + protected override void FreezeInternal() + { + base.FreezeInternal(); + primaryResult.Freeze(); + secondaryResult.Freeze(); + } + + public ResolveResult PrimaryResult { + get { + return primaryResult; + } + } + + public IEnumerable Results { + get { + yield return primaryResult; + yield return secondaryResult; + } + } + + public TypeResolveResult TypeResult { + get { + if (primaryResult is TypeResolveResult) + return (TypeResolveResult)primaryResult; + if (secondaryResult is TypeResolveResult) + return (TypeResolveResult)secondaryResult; + return null; + } + } + + public MixedResolveResult(ResolveResult primaryResult, ResolveResult secondaryResult) + : base(primaryResult.CallingClass, primaryResult.CallingMember, primaryResult.ResolvedType) + { + if (primaryResult == null) + throw new ArgumentNullException("primaryResult"); + if (secondaryResult == null) + throw new ArgumentNullException("secondaryResult"); + this.primaryResult = primaryResult; + this.secondaryResult = secondaryResult; + } + + public override FilePosition GetDefinitionPosition() + { + return primaryResult.GetDefinitionPosition(); + } + + public override List GetCompletionData(IProjectContent projectContent) + { + List result = primaryResult.GetCompletionData(projectContent); + List result2 = secondaryResult.GetCompletionData(projectContent); + if (result == null) return result2; + if (result2 == null) return result; + foreach (ICompletionEntry o in result2) { + if (!result.Contains(o)) + result.Add(o); + } + return result; + } + + public override ResolveResult Clone() + { + return new MixedResolveResult(primaryResult.Clone(), secondaryResult.Clone()); + } + + public override bool IsReferenceTo(IEntity entity) + { + return primaryResult.IsReferenceTo(entity) || secondaryResult.IsReferenceTo(entity); + } + } + #endregion + + #region LocalResolveResult + /// + /// The LocalResolveResult is used when an expression was a simple local variable + /// or parameter. + /// + /// + /// For fields in the current class, a MemberResolveResult is used, so "e" is not always + /// a LocalResolveResult. + /// + public class LocalResolveResult : ResolveResult + { + IField field; + + public LocalResolveResult(IMember callingMember, IField field) + : base(callingMember.DeclaringType, callingMember, field.ReturnType) + { + if (callingMember == null) + throw new ArgumentNullException("callingMember"); + if (field == null) + throw new ArgumentNullException("field"); + this.field = field; + if (!field.IsParameter && !field.IsLocalVariable) { + throw new ArgumentException("the field must either be a local variable-field or a parameter-field"); + } + } + + public LocalResolveResult(IMember callingMember, IParameter parameter) + : this(callingMember, new DefaultField.ParameterField(parameter.ReturnType, parameter.Name, parameter.Region, callingMember.DeclaringType)) + { + } + + public override ResolveResult Clone() + { + return new LocalResolveResult(this.CallingMember, this.Field); + } + + /// + /// Gets the field representing the local variable. + /// + public IField Field { + get { return field; } + } + + /// + /// Gets if the variable is a parameter (true) or a local variable (false). + /// + public bool IsParameter { + get { return field.IsParameter; } + } + + /// + /// Gets the name of the parameter/local variable. + /// + public string VariableName { + get { return field.Name; } + } + + /// + /// Gets th position where the parameter/local variable is declared. + /// + public DomRegion VariableDefinitionRegion { + get { return field.Region; } + } + + public override FilePosition GetDefinitionPosition() + { + ICompilationUnit cu = this.CallingClass.CompilationUnit; + if (cu == null) { + return FilePosition.Empty; + } + if (cu.FileName == null || cu.FileName.Length == 0) { + return FilePosition.Empty; + } + DomRegion reg = field.Region; + if (!reg.IsEmpty) { + return new FilePosition(cu.FileName, reg.BeginLine, reg.BeginColumn); + } else { + LoggingService.Warn("GetDefinitionPosition: field.Region is empty"); + return new FilePosition(cu.FileName); + } + } + + public override bool IsReferenceTo(IEntity entity) + { + IField f = entity as IField; + if (f != null && (f.IsLocalVariable || f.IsParameter)) { + return field.Region.BeginLine == f.Region.BeginLine + && field.Region.BeginColumn == f.Region.BeginColumn; + } + return base.IsReferenceTo(entity); + } + } + #endregion + + #region NamespaceResolveResult + /// + /// The NamespaceResolveResult is used when an expression was the name of a namespace. + /// is always null on a NamespaceReturnType. + /// + /// + /// Example expressions: + /// "System" + /// "System.Windows.Forms" + /// "using Win = System.Windows; Win.Forms" + /// + public class NamespaceResolveResult : ResolveResult + { + string name; + + public NamespaceResolveResult(IClass callingClass, IMember callingMember, string name) + : base(callingClass, callingMember, null) + { + if (name == null) + throw new ArgumentNullException("name"); + this.name = name; + } + + /// + /// Gets the name of the namespace. + /// + public string Name { + get { + return name; + } + } + + public override List GetCompletionData(IProjectContent projectContent) + { + return projectContent.GetNamespaceContents(name); + } + + public override ResolveResult Clone() + { + return new NamespaceResolveResult(this.CallingClass, this.CallingMember, this.Name); + } + } + #endregion + + #region IntegerLiteralResolveResult + /// + /// The IntegerLiteralResolveResult is used when an expression was an integer literal. + /// It is a normal ResolveResult with a type of "int", but does not provide + /// any code completion data. + /// + public class IntegerLiteralResolveResult : ResolveResult + { + public IntegerLiteralResolveResult(IClass callingClass, IMember callingMember, IReturnType systemInt32) + : base(callingClass, callingMember, systemInt32) + { + } + + public override List GetCompletionData(IProjectContent projectContent) + { + return null; + } + + public override ResolveResult Clone() + { + return new IntegerLiteralResolveResult(this.CallingClass, this.CallingMember, this.ResolvedType); + } + } + #endregion + + #region TypeResolveResult + /// + /// The TypeResolveResult is used when an expression was the name of a type. + /// This resolve result makes code completion show the static members instead + /// of the instance members. + /// + /// + /// Example expressions: + /// "System.EventArgs" + /// "using System; EventArgs" + /// + public class TypeResolveResult : ResolveResult + { + IClass resolvedClass; + + public TypeResolveResult(IClass callingClass, IMember callingMember, IClass resolvedClass) + : base(callingClass, callingMember, resolvedClass.DefaultReturnType) + { + this.resolvedClass = resolvedClass; + } + + public TypeResolveResult(IClass callingClass, IMember callingMember, IReturnType resolvedType, IClass resolvedClass) + : base(callingClass, callingMember, resolvedType) + { + this.resolvedClass = resolvedClass; + } + + public TypeResolveResult(IClass callingClass, IMember callingMember, IReturnType resolvedType) + : base(callingClass, callingMember, resolvedType) + { + this.resolvedClass = resolvedType.GetUnderlyingClass(); + } + + public override ResolveResult Clone() + { + return new TypeResolveResult(this.CallingClass, this.CallingMember, this.ResolvedType, this.ResolvedClass); + } + + /// + /// Gets the class corresponding to the resolved type. + /// This property can be null when the type has no class (for example a type parameter). + /// + public IClass ResolvedClass { + get { + return resolvedClass; + } + } + + public override List GetCompletionData(IProjectContent projectContent) + { + List ar = GetCompletionData(projectContent.Language, true); + if (resolvedClass != null) { + ar.AddRange(resolvedClass.GetCompoundClass().GetAccessibleTypes(CallingClass)); + } + return ar; + } + + public override FilePosition GetDefinitionPosition() + { + if (resolvedClass == null) { + return FilePosition.Empty; + } + ICompilationUnit cu = resolvedClass.CompilationUnit; + if (cu == null || cu.FileName == null || cu.FileName.Length == 0) { + return FilePosition.Empty; + } + DomRegion reg = resolvedClass.Region; + if (!reg.IsEmpty) + return new FilePosition(cu.FileName, reg.BeginLine, reg.BeginColumn); + else + return new FilePosition(cu.FileName); + } + + public override bool IsReferenceTo(IEntity entity) + { + IClass c = entity as IClass; + return c != null && resolvedClass != null + && resolvedClass.FullyQualifiedName == c.FullyQualifiedName + && resolvedClass.TypeParameters.Count == c.TypeParameters.Count; + } + } + #endregion + + #region MemberResolveResult + /// + /// The MemberResolveResult is used when an expression was a member + /// (field, property, event or method call). + /// + /// + /// Example expressions: + /// "(any expression).fieldName" + /// "(any expression).eventName" + /// "(any expression).propertyName" + /// "(any expression).methodName(arguments)" (methods only when method parameters are part of expression) + /// "using System; EventArgs.Empty" + /// "fieldName" (when fieldName is a field in the current class) + /// "new System.Windows.Forms.Button()" (constructors are also methods) + /// "SomeMethod()" (when SomeMethod is a method in the current class) + /// "System.Console.WriteLine(text)" + /// + public class MemberResolveResult : ResolveResult + { + IMember resolvedMember; + + public MemberResolveResult(IClass callingClass, IMember callingMember, IMember resolvedMember) + : base(callingClass, callingMember, resolvedMember.ReturnType) + { + if (resolvedMember == null) + throw new ArgumentNullException("resolvedMember"); + this.resolvedMember = resolvedMember; + } + + public override ResolveResult Clone() + { + return new MemberResolveResult(this.CallingClass, this.CallingMember, this.ResolvedMember) { + IsExtensionMethodCall = IsExtensionMethodCall + }; + } + + bool isExtensionMethodCall; + + public bool IsExtensionMethodCall { + get { return isExtensionMethodCall; } + set { + CheckBeforeMutation(); + isExtensionMethodCall = value; + } + } + + /// + /// Gets the member that was resolved. + /// + public IMember ResolvedMember { + get { return resolvedMember; } + } + + public override FilePosition GetDefinitionPosition() + { + return GetDefinitionPosition(resolvedMember); + } + + internal static FilePosition GetDefinitionPosition(IMember resolvedMember) + { + IClass declaringType = resolvedMember.DeclaringType; + if (declaringType == null) { + return FilePosition.Empty; + } + ICompilationUnit cu = declaringType.CompilationUnit; + if (cu == null) { + return FilePosition.Empty; + } + if (cu.FileName == null || cu.FileName.Length == 0) { + return FilePosition.Empty; + } + DomRegion reg = resolvedMember.Region; + if (!reg.IsEmpty) + return new FilePosition(cu.FileName, reg.BeginLine, reg.BeginColumn); + else + return new FilePosition(cu.FileName); + } + + public override bool IsReferenceTo(IEntity entity) + { + IClass c = entity as IClass; + if (c != null) { + IMethod m = resolvedMember as IMethod; + return m != null && m.IsConstructor + && m.DeclaringType.FullyQualifiedName == c.FullyQualifiedName + && m.DeclaringType.TypeParameters.Count == c.TypeParameters.Count; + } else { + return MemberLookupHelper.IsSimilarMember(resolvedMember, entity as IMember); + } + } + } + #endregion + + #region MethodResolveResult + public class MethodGroup : AbstractFreezable, IList + { + IList innerList; + bool isExtensionMethodGroup; + + public MethodGroup() : this(new List()) + { + } + + public MethodGroup(IList innerList) + { + if (innerList == null) + throw new ArgumentNullException("innerList"); + this.innerList = innerList; + } + + public bool IsExtensionMethodGroup { + get { return isExtensionMethodGroup; } + set { + CheckBeforeMutation(); + isExtensionMethodGroup = value; + } + } + + protected override void FreezeInternal() + { + base.FreezeInternal(); + innerList = FreezeList(innerList); + } + + public IMethod this[int index] { + get { return innerList[index]; } + set { innerList[index] = value; } + } + + public int Count { + get { return innerList.Count; } + } + + public bool IsReadOnly { + get { return innerList.IsReadOnly; } + } + + public int IndexOf(IMethod item) + { + return innerList.IndexOf(item); + } + + public void Insert(int index, IMethod item) + { + innerList.Insert(index, item); + } + + public void RemoveAt(int index) + { + innerList.RemoveAt(index); + } + + public void Add(IMethod item) + { + innerList.Add(item); + } + + public void Clear() + { + innerList.Clear(); + } + + public bool Contains(IMethod item) + { + return innerList.Contains(item); + } + + public void CopyTo(IMethod[] array, int arrayIndex) + { + innerList.CopyTo(array, arrayIndex); + } + + public bool Remove(IMethod item) + { + return innerList.Remove(item); + } + + public IEnumerator GetEnumerator() + { + return innerList.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + } + + /// + /// The MethodResolveResult is used when an expression was the name of a method, + /// but there were no parameters specified so the exact overload could not be found. + /// is always null on a MethodReturnType. + /// + /// + /// Example expressions: + /// "System.Console.WriteLine" + /// "a.Add" (where a is List<string>) + /// "SomeMethod" (when SomeMethod is a method in the current class) + /// + public class MethodGroupResolveResult : ResolveResult + { + string name; + IReturnType containingType; + IList possibleMethods; + + public bool IsVBNetAddressOf { get; set; } + + public MethodGroupResolveResult(IClass callingClass, IMember callingMember, IReturnType containingType, string name) + : base(callingClass, callingMember, null) + { + if (containingType == null) + throw new ArgumentNullException("containingType"); + if (name == null) + throw new ArgumentNullException("name"); + this.containingType = containingType; + this.name = name; + this.ResolvedType = new MethodGroupReturnType(); + } + + public MethodGroupResolveResult(IClass callingClass, IMember callingMember, IReturnType containingType, string name, + IList possibleMethods) + : base(callingClass, callingMember, null) + { + if (containingType == null) + throw new ArgumentNullException("containingType"); + if (name == null) + throw new ArgumentNullException("name"); + if (possibleMethods == null) + throw new ArgumentNullException("possibleMethods"); + this.containingType = containingType; + this.name = name; + this.possibleMethods = possibleMethods; + this.ResolvedType = new MethodGroupReturnType(); + } + + public MethodGroupResolveResult(IClass callingClass, IMember callingMember, IReturnType containingType, string name, + IList possibleMethods, bool isVBNet, bool isAddressOf) + : this(callingClass, callingMember, containingType, name, possibleMethods) + { + IMethod parameterlessMethod = possibleMethods.SelectMany(list => list).FirstOrDefault(m => !m.Parameters.Any());; + if (isVBNet && !isAddressOf && parameterlessMethod != null) + this.ResolvedType = parameterlessMethod.ReturnType; + } + + public override ResolveResult Clone() + { + return new MethodGroupResolveResult(this.CallingClass, this.CallingMember, this.ContainingType, this.Name, this.Methods); + } + + protected override void FreezeInternal() + { + base.FreezeInternal(); + if (possibleMethods != null) { + possibleMethods = FreezeList(possibleMethods); + } + } + + /// + /// Gets the name of the method. + /// + public string Name { + get { return name; } + } + + /// + /// Gets the class that contains the method. + /// This property cannot be null. + /// + public IReturnType ContainingType { + get { return containingType; } + } + + /// + /// The list of possible methods. + /// + public IList Methods { + get { + if (possibleMethods == null) { + possibleMethods = FreezeList( + new MethodGroup[] { + new MethodGroup( + containingType.GetMethods().FindAll((IMethod m) => m.Name == this.name) + ) + }); + } + return possibleMethods; + } + } + + public IMethod GetMethodIfSingleOverload() + { + if (this.Methods.Count > 0 && this.Methods[0].Count == 1) + return this.Methods[0][0]; + else + return null; + } + + public IMethod GetMethodWithEmptyParameterList() + { + if (this.Methods.Count > 0 && !IsVBNetAddressOf) { + return this.Methods + .SelectMany(group => group.Select(item => item)) + .FirstOrDefault(i => i.Parameters.Count == 0); + } + + return null; + } + + public override FilePosition GetDefinitionPosition() + { + IMethod m = GetMethodIfSingleOverload(); + IMethod m2 = GetMethodWithEmptyParameterList(); + if (m != null) + return MemberResolveResult.GetDefinitionPosition(m); + else if (m2 != null) + return MemberResolveResult.GetDefinitionPosition(m2); + else + return base.GetDefinitionPosition(); + } + + public override bool IsReferenceTo(IEntity entity) + { + return MemberLookupHelper.IsSimilarMember(GetMethodIfSingleOverload(), entity as IMember); + } + } + #endregion + + #region VBBaseOrThisReferenceInConstructorResolveResult + /// + /// Is used for "MyBase" or "Me" in VB constructors to show "New" in the completion list. + /// + public class VBBaseOrThisReferenceInConstructorResolveResult : ResolveResult + { + public VBBaseOrThisReferenceInConstructorResolveResult(IClass callingClass, IMember callingMember, IReturnType referencedType) + : base(callingClass, callingMember, referencedType) + { + } + + public override List GetCompletionData(IProjectContent projectContent) + { + List res = base.GetCompletionData(projectContent); + foreach (IMethod m in this.ResolvedType.GetMethods()) { + if (m.IsConstructor && !m.IsStatic && m.IsAccessible(this.CallingClass, true)) + res.Add(m); + } + return res; + } + + public override ResolveResult Clone() + { + return new VBBaseOrThisReferenceInConstructorResolveResult(this.CallingClass, this.CallingMember, this.ResolvedType); + } + } + #endregion + + #region BaseResolveResult + /// + /// Is used for "base"/"MyBase" expression. + /// The completion list always shows protected members. + /// + public class BaseResolveResult : ResolveResult + { + public BaseResolveResult(IClass callingClass, IMember callingMember, IReturnType baseClassType) + : base(callingClass, callingMember, baseClassType) + { + } + + public override List GetCompletionData(IProjectContent projectContent) + { + if (this.ResolvedType == null) return null; + List res = new List(); + + foreach (IMember m in MemberLookupHelper.GetAccessibleMembers(this.ResolvedType, this.CallingClass, projectContent.Language, true)) { + if (projectContent.Language.ShowMember(m, false)) + res.Add(m); + } + + if (this.CallingClass != null) { + AddExtensions(projectContent.Language, res.Add, this.CallingClass, this.ResolvedType); + } + + return res; + } + + public override ResolveResult Clone() + { + return new BaseResolveResult(this.CallingClass, this.CallingMember, this.ResolvedType); + } + } + #endregion + + #region DelegateCallResolveResult + /// + /// Is used for calls to delegates/events. + /// + public class DelegateCallResolveResult : ResolveResult + { + IMethod delegateInvokeMethod; + ResolveResult targetRR; + + protected override void FreezeInternal() + { + base.FreezeInternal(); + delegateInvokeMethod.Freeze(); + targetRR.Freeze(); + } + + public DelegateCallResolveResult(ResolveResult targetRR, IMethod delegateInvokeMethod) + : base(targetRR.CallingClass, targetRR.CallingMember, delegateInvokeMethod.ReturnType) + { + this.targetRR = targetRR; + this.delegateInvokeMethod = delegateInvokeMethod; + } + + /// + /// Gets the Invoke() method of the delegate. + /// + public IMethod DelegateInvokeMethod { + get { return delegateInvokeMethod; } + } + + /// + /// Gets the type of the delegate. + /// + public IReturnType DelegateType { + get { return targetRR.ResolvedType; } + } + + /// + /// Gets the resolve result referring to the delegate. + /// + public ResolveResult Target { + get { return targetRR; } + } + + public override ResolveResult Clone() + { + return new DelegateCallResolveResult(targetRR, delegateInvokeMethod); + } + + public override FilePosition GetDefinitionPosition() + { + return targetRR.GetDefinitionPosition(); + } + + public override bool IsReferenceTo(ICSharpCode.SharpDevelop.Dom.IEntity entity) + { + return targetRR.IsReferenceTo(entity); + } + } + #endregion + + #region UnknownIdentifierResolveResult + /// + /// Used for unknown identifiers. + /// + public class UnknownIdentifierResolveResult : ResolveResult + { + string identifier; + + public UnknownIdentifierResolveResult(IClass callingClass, IMember callingMember, string identifier) + : base(callingClass, callingMember, null) + { + this.identifier = identifier; + } + + public string Identifier { + get { return identifier; } + } + + public override bool IsValid { + get { return false; } + } + + public override ResolveResult Clone() + { + return new UnknownIdentifierResolveResult(this.CallingClass, this.CallingMember, this.Identifier); + } + } + #endregion + + #region UnknownMethodResolveResult + /// + /// Used for calls to unknown methods. + /// + public class UnknownMethodResolveResult : ResolveResult + { + string callName; + bool isStaticContext; + List arguments; + IReturnType target; + + public UnknownMethodResolveResult(IClass callingClass, IMember callingMember, IReturnType target, string callName, bool isStaticContext, List arguments) + : base(callingClass, callingMember, null) + { + this.target = target == null ? (callingClass == null ? null : callingClass.DefaultReturnType) : target; + this.callName = callName; + this.arguments = arguments; + this.isStaticContext = isStaticContext; + } + + public bool IsStaticContext { + get { return isStaticContext; } + } + + public string CallName { + get { return callName; } + } + + public IReturnType Target { + get { return target; } + } + + public List Arguments { + get { return arguments; } + } + + public override bool IsValid { + get { return false; } + } + + public override ResolveResult Clone() + { + return new UnknownMethodResolveResult(this.CallingClass, this.CallingMember, this.target, this.callName, this.isStaticContext, this.arguments); + } + } + #endregion + + #region UnknownConstructorCallResolveResult + /// + /// Used for constructor calls on unknown types. + /// + public class UnknownConstructorCallResolveResult : ResolveResult + { + string typeName; + + public UnknownConstructorCallResolveResult(IClass callingClass, IMember callingMember, string typeName) + : base(callingClass, callingMember, null) + { + this.typeName = typeName; + } + + public string TypeName { + get { return typeName; } + } + + public override bool IsValid { + get { return false; } + } + + public override ResolveResult Clone() + { + return new UnknownConstructorCallResolveResult(this.CallingClass, this.CallingMember, this.TypeName); + } + } + #endregion +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/SignatureComparer.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/SignatureComparer.cs new file mode 100644 index 000000000..c8425e445 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/SignatureComparer.cs @@ -0,0 +1,39 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public class SignatureComparer : IEqualityComparer + { + ParameterListComparer parameterListComparer = new ParameterListComparer(); + + public bool Equals(IMember x, IMember y) + { + if (x.EntityType != y.EntityType) + return false; + + if (x.Name != y.Name) + return false; + + if (x is IMethod && y is IMethod) + return parameterListComparer.Equals(x as IMethod, y as IMethod); + + return true; + } + + public int GetHashCode(IMember obj) + { + int hashCode = obj.Name.GetHashCode(); + + if (obj is IMethod) + hashCode ^= parameterListComparer.GetHashCode(obj as IMethod); + + return hashCode; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Tag.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Tag.cs new file mode 100644 index 000000000..632f7b362 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/Tag.cs @@ -0,0 +1,46 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom +{ + public sealed class TagComment : Immutable + { + string key; + + public string Key { + get { + return key; + } + } + + string commentString; + DomRegion region; + + public string CommentString { + get { + return commentString; + } + } + + public DomRegion Region { + get { + return region; + } + } + + public TagComment(string key, DomRegion region) + { + this.key = key; + this.region = region; + } + + public TagComment(string key, DomRegion region, string commentString) + { + this.key = key; + this.region = region; + this.commentString = commentString; + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/IVBNetOptionProvider.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/IVBNetOptionProvider.cs new file mode 100644 index 000000000..a4989dc50 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/IVBNetOptionProvider.cs @@ -0,0 +1,24 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom.VBNet +{ + /// + /// Description of IVBNetOptionProvider. + /// + public interface IVBNetOptionProvider + { + bool? OptionInfer { get; } + bool? OptionStrict { get; } + bool? OptionExplicit { get; } + CompareKind? OptionCompare { get; } + } + + public enum CompareKind + { + Binary, + Text + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/VBNetAmbience.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/VBNetAmbience.cs new file mode 100644 index 000000000..a46302521 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/VBNetAmbience.cs @@ -0,0 +1,627 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.SharpDevelop.Dom.VBNet +{ + public class VBNetAmbience : AbstractAmbience + { + public static IDictionary TypeConversionTable { + get { return ICSharpCode.NRefactory.Ast.TypeReference.PrimitiveTypesVBReverse; } + } + + string GetModifier(IEntity decoration) + { + StringBuilder builder = new StringBuilder(); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (decoration.IsStatic) { + builder.Append("Shared "); + } + if (decoration.IsAbstract) { + builder.Append("MustOverride "); + } else if (decoration.IsSealed) { + builder.Append("NotOverridable "); + } + if (decoration.IsVirtual) { + builder.Append("Overridable "); + } else if (decoration.IsOverride) { + builder.Append("Overrides "); + } + if (decoration.IsNew) { + builder.Append("Shadows "); + } + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + return builder.ToString(); + } + + public override string ConvertAccessibility(ModifierEnum accessibility) + { + StringBuilder builder = new StringBuilder(); + if (ShowAccessibility) { + if ((accessibility & ModifierEnum.Public) == ModifierEnum.Public) { + builder.Append("Public"); + } else if ((accessibility & ModifierEnum.Private) == ModifierEnum.Private) { + builder.Append("Private"); + } else if ((accessibility & (ModifierEnum.Protected | ModifierEnum.Internal)) == (ModifierEnum.Protected | ModifierEnum.Internal)) { + builder.Append("Protected Friend"); + } else if ((accessibility & ModifierEnum.Internal) == ModifierEnum.Internal) { + builder.Append("Friend"); + } else if ((accessibility & ModifierEnum.Protected) == ModifierEnum.Protected) { + builder.Append("Protected"); + } + builder.Append(' '); + } + return builder.ToString(); + } + + public override string Convert(IClass c) + { + CheckThread(); + + StringBuilder builder = new StringBuilder(); + + builder.Append(ConvertAccessibility(c.Modifiers)); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (ShowModifiers) { + if (c.IsSealed) { + if (c.ClassType == ClassType.Class) { + builder.Append("NotInheritable "); + } + } else if (c.IsAbstract && c.ClassType != ClassType.Interface) { + builder.Append("MustInherit "); + } + } + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (ShowDefinitionKeyWord) { + switch (c.ClassType) { + case ClassType.Delegate: + builder.Append("Delegate "); + if (ShowReturnType) { + foreach (IMethod m in c.Methods) { + if (m.Name != "Invoke") { + continue; + } + + if (m.ReturnType == null || m.ReturnType.FullyQualifiedName == "System.Void") { + builder.Append("Sub"); + } else { + builder.Append("Function"); + } + } + } + break; + case ClassType.Class: + builder.Append("Class"); + break; + case ClassType.Module: + builder.Append("Module"); + break; + case ClassType.Struct: + builder.Append("Structure"); + break; + case ClassType.Interface: + builder.Append("Interface"); + break; + case ClassType.Enum: + builder.Append("Enum"); + break; + } + builder.Append(' '); + } + + AppendClassNameWithTypeParameters(builder, c, UseFullyQualifiedMemberNames, true, null); + + if (ShowParameterList && c.ClassType == ClassType.Delegate) { + builder.Append("("); + if (IncludeHtmlMarkup) builder.Append("
"); + + foreach (IMethod m in c.Methods) { + if (m.Name != "Invoke") continue; + + for (int i = 0; i < m.Parameters.Count; ++i) { + if (IncludeHtmlMarkup) builder.Append("   "); + + builder.Append(Convert(m.Parameters[i])); + if (i + 1 < m.Parameters.Count) builder.Append(", "); + + if (IncludeHtmlMarkup) builder.Append("
"); + } + } + + builder.Append(")"); + } + if (ShowReturnType && c.ClassType == ClassType.Delegate) { + foreach (IMethod m in c.Methods) { + if (m.Name != "Invoke") continue; + + if (m.ReturnType == null || m.ReturnType.FullyQualifiedName == "System.Void") { + } else { + if (ShowReturnType) { + builder.Append(" As "); + builder.Append(Convert(m.ReturnType)); + } + } + } + } else if (ShowInheritanceList && c.ClassType != ClassType.Delegate) { + if (c.BaseTypes.Count > 0) { + builder.Append(" Inherits "); + for (int i = 0; i < c.BaseTypes.Count; ++i) { + builder.Append(c.BaseTypes[i]); + if (i + 1 < c.BaseTypes.Count) { + builder.Append(", "); + } + } + } + } + + return builder.ToString(); + } + + void AppendTypeNameForFullyQualifiedMemberName(StringBuilder builder, IReturnType declaringType) + { + if (UseFullyQualifiedMemberNames && declaringType != null) { + AppendReturnType(builder, declaringType, true); + builder.Append('.'); + } + } + + void AppendClassNameWithTypeParameters(StringBuilder builder, IClass c, bool fullyQualified, bool isConvertingClassName, IList typeArguments) + { + if (isConvertingClassName && IncludeHtmlMarkup) { + builder.Append(""); + } + if (fullyQualified) { + if (c.DeclaringType != null) { + AppendClassNameWithTypeParameters(builder, c.DeclaringType, fullyQualified, false, typeArguments); + builder.Append('.'); + builder.Append(c.Name); + } else { + builder.Append(c.FullyQualifiedName); + } + } else { + builder.Append(c.Name); + } + if (isConvertingClassName && IncludeHtmlMarkup) { + builder.Append(""); + } + // skip type parameters that belong to declaring types (in DOM, inner classes repeat type parameters from outer classes) + int skippedTypeParameterCount = c.DeclaringType != null ? c.DeclaringType.TypeParameters.Count : 0; + // show type parameters for classes only if ShowTypeParameterList is set; but always show them in other cases. + if ((ShowTypeParameterList || !isConvertingClassName) && c.TypeParameters.Count > skippedTypeParameterCount) { + builder.Append("(Of "); + for (int i = skippedTypeParameterCount; i < c.TypeParameters.Count; ++i) { + if (i > skippedTypeParameterCount) + builder.Append(", "); + if (typeArguments != null && i < typeArguments.Count) + AppendReturnType(builder, typeArguments[i], false); + else + builder.Append(ConvertTypeParameter(c.TypeParameters[i])); + } + builder.Append(')'); + } + } + + public override string ConvertEnd(IClass c) + { + if (c == null) + throw new ArgumentNullException("c"); + + StringBuilder builder = new StringBuilder(); + + builder.Append("End "); + + switch (c.ClassType) { + case ClassType.Delegate: + builder.Append("Delegate"); + break; + case ClassType.Class: + builder.Append("Class"); + break; + case ClassType.Module: + builder.Append("Module"); + break; + case ClassType.Struct: + builder.Append("Structure"); + break; + case ClassType.Interface: + builder.Append("Interface"); + break; + case ClassType.Enum: + builder.Append("Enum"); + break; + } + + return builder.ToString(); + } + + public override string Convert(IField field) + { + CheckThread(); + + if (field == null) + throw new ArgumentNullException("field"); + + StringBuilder builder = new StringBuilder(); + + builder.Append(ConvertAccessibility(field.Modifiers)); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (ShowModifiers) { + if (field.IsConst) { + builder.Append("Const "); + } else if (field.IsStatic) { + builder.Append("Shared "); + } + } + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + AppendTypeNameForFullyQualifiedMemberName(builder, field.DeclaringTypeReference); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + builder.Append(field.Name); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (field.ReturnType != null && ShowReturnType) { + builder.Append(" As "); + builder.Append(Convert(field.ReturnType)); + } + + return builder.ToString(); + } + + public override string Convert(IProperty property) + { + CheckThread(); + + StringBuilder builder = new StringBuilder(); + + builder.Append(ConvertAccessibility(property.Modifiers)); + + if (ShowModifiers) { + builder.Append(GetModifier(property)); + + if (property.IsIndexer) { + builder.Append("Default "); + } + + if (property.CanGet && !property.CanSet) { + builder.Append("ReadOnly "); + } + if (property.CanSet && !property.CanGet) { + builder.Append("WriteOnly "); + } + } + + if (ShowDefinitionKeyWord) { + builder.Append("Property "); + } + + AppendTypeNameForFullyQualifiedMemberName(builder, property.DeclaringTypeReference); + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + builder.Append(property.Name); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (ShowParameterList && property.Parameters.Count > 0) { + builder.Append("("); + if (IncludeHtmlMarkup) builder.Append("
"); + + for (int i = 0; i < property.Parameters.Count; ++i) { + if (IncludeHtmlMarkup) builder.Append("   "); + builder.Append(Convert(property.Parameters[i])); + if (i + 1 < property.Parameters.Count) { + builder.Append(", "); + } + if (IncludeHtmlMarkup) builder.Append("
"); + } + + builder.Append(')'); + } + + if (property.ReturnType != null && ShowReturnType) { + builder.Append(" As "); + builder.Append(Convert(property.ReturnType)); + } + + return builder.ToString(); + } + + public override string Convert(IEvent e) + { + CheckThread(); + + if (e == null) + throw new ArgumentNullException("e"); + + StringBuilder builder = new StringBuilder(); + + builder.Append(ConvertAccessibility(e.Modifiers)); + + if (ShowModifiers) { + builder.Append(GetModifier(e)); + } + + if (ShowDefinitionKeyWord) { + builder.Append("Event "); + } + + AppendTypeNameForFullyQualifiedMemberName(builder, e.DeclaringTypeReference); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + builder.Append(e.Name); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (e.ReturnType != null && ShowReturnType) { + builder.Append(" As "); + builder.Append(Convert(e.ReturnType)); + } + + return builder.ToString(); + } + + public override string Convert(IMethod m) + { + CheckThread(); + + StringBuilder builder = new StringBuilder(); + if (ShowModifiers && m.IsExtensionMethod) { + builder.Append(" "); + } + + builder.Append(ConvertAccessibility(m.Modifiers)); // show visibility + + if (ShowModifiers) { + builder.Append(GetModifier(m)); + } + if (ShowDefinitionKeyWord) { + if (m.ReturnType == null || m.ReturnType.FullyQualifiedName == "System.Void") { + builder.Append("Sub "); + } else { + builder.Append("Function "); + } + } + + AppendTypeNameForFullyQualifiedMemberName(builder, m.DeclaringTypeReference); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + builder.Append(m.IsConstructor ? "New" : m.Name); + + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (ShowTypeParameterList && m.TypeParameters.Count > 0) { + builder.Append("(Of "); + for (int i = 0; i < m.TypeParameters.Count; ++i) { + if (i > 0) builder.Append(", "); + builder.Append(ConvertTypeParameter(m.TypeParameters[i])); + } + builder.Append(')'); + } + + if (ShowParameterList) { + builder.Append("("); + if (IncludeHtmlMarkup) builder.Append("
"); + + for (int i = 0; i < m.Parameters.Count; ++i) { + if (IncludeHtmlMarkup) builder.Append("   "); + builder.Append(Convert(m.Parameters[i])); + if (i + 1 < m.Parameters.Count) { + builder.Append(", "); + } + if (IncludeHtmlMarkup) builder.Append("
"); + } + + builder.Append(')'); + } + + if (ShowReturnType && m.ReturnType != null && m.ReturnType.FullyQualifiedName != "System.Void") { + builder.Append(" As "); + builder.Append(Convert(m.ReturnType)); + } + + return builder.ToString(); + } + + string ConvertTypeParameter(ITypeParameter tp) + { + if (tp.BoundTo != null) + return Convert(tp.BoundTo); + else + return tp.Name; + } + + public override string ConvertEnd(IMethod m) + { + if (m == null) + throw new ArgumentNullException("m"); + + if (m.ReturnType == null || m.ReturnType.FullyQualifiedName == "System.Void") { + return "End Sub"; + } else { + return "End Function"; + } + } + + public override string Convert(IReturnType returnType) + { + CheckThread(); + + if (returnType == null) { + return String.Empty; + } + + returnType = returnType.GetDirectReturnType(); + + StringBuilder builder = new StringBuilder(); + + AppendReturnType(builder, returnType, false); + + return builder.ToString(); + } + + void AppendReturnType(StringBuilder builder, IReturnType returnType, bool forceFullyQualifiedName) + { + IReturnType arrayReturnType = returnType; + returnType = GetElementType(returnType); + + if (returnType == null) + return; + + string fullName = returnType.FullyQualifiedName; + string shortName; + bool isConstructedType = returnType.IsConstructedReturnType; + if (fullName != null && !isConstructedType && TypeConversionTable.TryGetValue(fullName, out shortName)) { + builder.Append(shortName); + } else { + IClass c = returnType.GetUnderlyingClass(); + + if (c != null) { + IList ta = isConstructedType ? returnType.CastToConstructedReturnType().TypeArguments : null; + AppendClassNameWithTypeParameters(builder, c, forceFullyQualifiedName || UseFullyQualifiedTypeNames, false, ta); + } else { + if (UseFullyQualifiedTypeNames || forceFullyQualifiedName) { + builder.Append(fullName); + } else { + builder.Append(returnType.Name); + } + if (isConstructedType) { + builder.Append("(Of "); + IList ta = returnType.CastToConstructedReturnType().TypeArguments; + for (int i = 0; i < ta.Count; ++i) { + if (i > 0) builder.Append(", "); + AppendReturnType(builder, ta[i], false); + } + builder.Append(')'); + } + } + } + + UnpackArrayType(builder, arrayReturnType); + } + + static IReturnType GetElementType(IReturnType potentialArrayType) + { + if (potentialArrayType == null) + return null; + ArrayReturnType result; + while ((result = potentialArrayType.CastToArrayReturnType()) != null) { + potentialArrayType = result.ArrayElementType; + } + return potentialArrayType; + } + + static void UnpackArrayType(StringBuilder builder, IReturnType returnType) + { + if (returnType.IsArrayReturnType) { + builder.Append('('); + int dimensions = returnType.CastToArrayReturnType().ArrayDimensions; + for (int i = 1; i < dimensions; ++i) { + builder.Append(','); + } + builder.Append(')'); + UnpackArrayType(builder, returnType.CastToArrayReturnType().ArrayElementType); + } + } + + public override string Convert(IParameter param) + { + CheckThread(); + + if (param == null) + throw new ArgumentNullException("param"); + + StringBuilder builder = new StringBuilder(); + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (param.IsOptional) { + builder.Append("Optional "); + } + if (param.IsRef || param.IsOut) { + builder.Append("ByRef "); + } else if (param.IsParams) { + builder.Append("ParamArray "); + } + if (IncludeHtmlMarkup) { + builder.Append(""); + } + + if (ShowParameterNames) { + builder.Append(param.Name); + builder.Append(" As "); + } + + builder.Append(Convert(param.ReturnType)); + + return builder.ToString(); + } + + public override string WrapAttribute(string attribute) + { + return "<" + attribute + ">"; + } + + public override string WrapComment(string comment) + { + return "' " + comment; + } + + public override string GetIntrinsicTypeName(string dotNetTypeName) + { + string shortName; + if (TypeConversionTable.TryGetValue(dotNetTypeName, out shortName)) { + return shortName; + } + return dotNetTypeName; + } + } + +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/VBNetCompilationUnit.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/VBNetCompilationUnit.cs new file mode 100644 index 000000000..1db27f49a --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/VBNetCompilationUnit.cs @@ -0,0 +1,51 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.SharpDevelop.Dom.VBNet +{ + /// + /// Description of VBNetCompilationUnit. + /// + public class VBNetCompilationUnit : DefaultCompilationUnit, IVBNetOptionProvider + { + IVBNetOptionProvider projectOptionProvider; + + public VBNetCompilationUnit(IProjectContent projectContent) + : base(projectContent) + { + if (projectContent.Project is IVBNetOptionProvider) + projectOptionProvider = projectContent.Project as IVBNetOptionProvider; + else { + infer = false; + strict = false; + @explicit = true; + compare = CompareKind.Binary; + } + } + + bool? infer, strict, @explicit; + CompareKind? compare; + + public bool? OptionInfer { + get { return infer ?? projectOptionProvider.OptionInfer; } + set { infer = value; } + } + + public bool? OptionStrict { + get { return strict ?? projectOptionProvider.OptionStrict; } + set { strict = value; } + } + + public bool? OptionExplicit { + get { return @explicit ?? projectOptionProvider.OptionExplicit; } + set { @explicit = value; } + } + + public CompareKind? OptionCompare { + get { return compare ?? projectOptionProvider.OptionCompare; } + set { compare = value; } + } + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/VBNetExpressionFinder.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/VBNetExpressionFinder.cs new file mode 100644 index 000000000..2682270d0 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/VBNet/VBNetExpressionFinder.cs @@ -0,0 +1,269 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Parser; +using ICSharpCode.NRefactory.Parser.VB; + +namespace ICSharpCode.SharpDevelop.Dom.VBNet +{ + /// + /// Description of VBNetExpressionFinder. + /// + public class VBNetExpressionFinder : IExpressionFinder + { + ParseInformation parseInformation; + IProjectContent projectContent; + ILexer lexer; + Location targetPosition; + List lineOffsets; + + int LocationToOffset(Location location) + { + if (location.Line <= 0 || location.Line >= lineOffsets.Count) + return -1; + return lineOffsets[location.Line - 1] + location.Column - 1; + } + + Location OffsetToLocation(int offset) + { + int lineNumber = lineOffsets.BinarySearch(offset); + if (lineNumber < 0) { + lineNumber = (~lineNumber) - 1; + } + return new Location(offset - lineOffsets[lineNumber] + 1, lineNumber + 1); + } + + public VBNetExpressionFinder(ParseInformation parseInformation) + { + this.parseInformation = parseInformation; + if (parseInformation != null && parseInformation.CompilationUnit != null) { + projectContent = parseInformation.CompilationUnit.ProjectContent; + } else { + projectContent = DefaultProjectContent.DummyProjectContent; + } + } + + public ExpressionResult FindExpression(string text, int offset) + { + Init(text, offset); + + ExpressionFinder p = new ExpressionFinder(); + lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, new StringReader(text)); + Token t = lexer.NextToken(); + + // put all tokens in front of targetPosition into the EF-Parser + while (t.EndLocation < targetPosition) { + p.InformToken(t); + t = lexer.NextToken(); + } + + // put current token into EF-Parser if it cannot be continued (is simple operator) + if (t.EndLocation == targetPosition && ((t.Kind <= Tokens.ColonAssign && t.Kind > Tokens.Identifier) || t.Kind == Tokens.EOL)) { + p.InformToken(t); + t = lexer.NextToken(); + } + + // make sure semantic actions are executed + p.Advance(); + + // remember current state, we'll use it to determine the context + var block = p.CurrentBlock; + + ExpressionContext context = p.IsIdentifierExpected && !p.IsMissingModifier ? ExpressionContext.IdentifierExpected : GetContext(block); + + BitArray expectedSet; + + try { + expectedSet = p.GetExpectedSet(); + } catch (InvalidOperationException) { + expectedSet = null; + } + + // put current token into EF-Parser + if (t.Location < targetPosition) { + p.InformToken(t); + } + + if (p.Errors.Any()) { + foreach (var e in p.Errors) + LoggingService.Warn("not expected: " + e); + } + + if (p.NextTokenIsPotentialStartOfExpression) + return new ExpressionResult("", new DomRegion(targetPosition.Line, targetPosition.Column), context, expectedSet); + + int lastExpressionStartOffset = LocationToOffset(p.CurrentBlock.lastExpressionStart); + + if (lastExpressionStartOffset < 0) + return new ExpressionResult("", new DomRegion(targetPosition.Line, targetPosition.Column), context, expectedSet); + + return MakeResult(text, lastExpressionStartOffset, offset, context, expectedSet); + } + + ExpressionResult MakeResult(string text, int startOffset, int endOffset, ExpressionContext context, BitArray expectedKeywords) + { + // partial/incomplete expressions (especially between comments) need this hack. + // see http://community.sharpdevelop.net/forums/t/11951.aspx (first post) + if (startOffset > endOffset) { + int tmp = startOffset; + startOffset = endOffset; + endOffset = tmp; + } + + return new ExpressionResult(TrimComment(text.Substring(startOffset, endOffset - startOffset)).Trim(), + DomRegion.FromLocation(OffsetToLocation(startOffset), OffsetToLocation(endOffset)), + context, expectedKeywords); + } + + string TrimComment(string text) + { + bool inString = false; + int i = 0; + + while (i < text.Length) { + char ch = text[i]; + + if (ch == '"') + inString = !inString; + + bool isInWord = (i > 0 && char.IsLetterOrDigit(text[i - 1])) + || (i + 1 < text.Length && char.IsLetterOrDigit(text[i + 1])); + + if ((ch == '\'' || ch == '_') && !inString && !isInWord) { + int eol = text.IndexOfAny(new[] { '\r', '\n' }, i); + + if (eol > -1) { + if(text[eol] == '\r' && eol + 1 < text.Length && text[eol + 1] == '\n') + eol++; + + text = text.Remove(i, eol - i); + } else { + text = text.Remove(i); + } + + continue; + } + + i++; + } + + return text; + } + + void Init(string text, int offset) + { + lineOffsets = new List(); + lineOffsets.Add(0); + for (int i = 0; i < text.Length; i++) { + if (i == offset) { + targetPosition = new Location(offset - lineOffsets[lineOffsets.Count - 1] + 1, lineOffsets.Count); + } + if (text[i] == '\n') { + lineOffsets.Add(i + 1); + } else if (text[i] == '\r') { + if (i + 1 < text.Length && text[i + 1] != '\n') { + lineOffsets.Add(i + 1); + } + } + } + if (offset == text.Length) { + targetPosition = new Location(offset - lineOffsets[lineOffsets.Count - 1] + 1, lineOffsets.Count); + } + } + + ExpressionContext GetContext(Block block) + { + switch (block.context) { + case Context.Global: + return ExpressionContext.Global; + case Context.TypeDeclaration: + return ExpressionContext.TypeDeclaration; + case Context.Type: + return ExpressionContext.Type; + case Context.Body: + return ExpressionContext.MethodBody; + case Context.Importable: + return ExpressionContext.Importable; + case Context.ObjectCreation: + return ExpressionContext.ObjectCreation; + case Context.Parameter: + return ExpressionContext.Parameter; + } + + return ExpressionContext.Default; + } + + public ExpressionResult FindFullExpression(string text, int offset) + { + Init(text, offset); + + ExpressionFinder p = new ExpressionFinder(); + lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, new StringReader(text)); + Token t; + + Block block = Block.Default; + + var expressionDelimiters = new[] { Tokens.EOL, Tokens.Colon, Tokens.Dot, Tokens.TripleDot, Tokens.DotAt }; + + while (true) { + t = lexer.NextToken(); + p.InformToken(t); + + if (block == Block.Default && t.EndLocation > targetPosition) + block = p.CurrentBlock; + if (block != Block.Default && (block.isClosed || expressionDelimiters.Contains(t.Kind) && block == p.CurrentBlock)) + break; + if (t.Kind == Tokens.EOF) + break; + } + + if (p.Errors.Any()) { + foreach (var e in p.Errors) + LoggingService.Warn("not expected: " + e); + } + + BitArray expectedSet; + + try { + expectedSet = p.GetExpectedSet(); + } catch (InvalidOperationException) { + expectedSet = null; + } + + int tokenOffset; + if (t == null || t.Kind == Tokens.EOF) + tokenOffset = text.Length; + else + tokenOffset = LocationToOffset(t.Location); + + int lastExpressionStartOffset = LocationToOffset(block.lastExpressionStart); + if (lastExpressionStartOffset >= 0) { + if (offset < tokenOffset) { + // offset is in front of this token + return MakeResult(text, lastExpressionStartOffset, tokenOffset, GetContext(block), expectedSet); + } else { + // offset is IN this token + return MakeResult(text, lastExpressionStartOffset, offset, GetContext(block), expectedSet); + } + } else { + return new ExpressionResult(null, GetContext(block)); + } + } + + public string RemoveLastPart(string expression) + { + return expression; + } + + #region Helpers + + #endregion + } +} diff --git a/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/XmlDoc.cs b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/XmlDoc.cs new file mode 100644 index 000000000..6df1e7cc6 --- /dev/null +++ b/Libraries/ICSharpCode.SharpDevelop.Dom/Project/Src/XmlDoc.cs @@ -0,0 +1,356 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; + +namespace ICSharpCode.SharpDevelop.Dom +{ + /// + /// Class capable of loading xml documentation files. XmlDoc automatically creates a + /// binary cache for big xml files to reduce memory usage. + /// + public sealed class XmlDoc : IDisposable + { + static readonly List xmlDocLookupDirectories = new List { + System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory() + }; + + public static IList XmlDocLookupDirectories { + get { return xmlDocLookupDirectories; } + } + + struct IndexEntry : IComparable + { + public int HashCode; + public int FileLocation; + + public int CompareTo(IndexEntry other) + { + return HashCode.CompareTo(other.HashCode); + } + + public IndexEntry(int HashCode, int FileLocation) + { + this.HashCode = HashCode; + this.FileLocation = FileLocation; + } + } + + Dictionary xmlDescription = new Dictionary(); + IndexEntry[] index; // SORTED array of index entries + Queue keyCacheQueue; + + const int cacheLength = 150; // number of strings to cache when working in file-mode + + void ReadMembersSection(XmlReader reader) + { + while (reader.Read()) { + switch (reader.NodeType) { + case XmlNodeType.EndElement: + if (reader.LocalName == "members") { + return; + } + break; + case XmlNodeType.Element: + if (reader.LocalName == "member") { + string memberAttr = reader.GetAttribute(0); + string innerXml = reader.ReadInnerXml(); + xmlDescription[memberAttr] = innerXml; + } + break; + } + } + } + + public string GetDocumentation(string key) + { + if (xmlDescription == null) + throw new ObjectDisposedException("XmlDoc"); + lock (xmlDescription) { + string result; + if (xmlDescription.TryGetValue(key, out result)) + return result; + if (index == null) + return null; + return LoadDocumentation(key); + } + } + + #region Save binary files + // FILE FORMAT FOR BINARY DOCUMENTATION + // long magic = 0x4244636f446c6d58 (identifies file type = 'XmlDocDB') + const long magic = 0x4244636f446c6d58; + // short version = 2 (file version) + const short version = 2; + // long fileDate (last change date of xml file in DateTime ticks) + // int testHashCode = magicTestString.GetHashCode() // (check if hash-code implementation is compatible) + // int entryCount (count of entries) + // int indexPointer (points to location where index starts in the file) + // { + // string key (documentation key as length-prefixed string) + // string docu (xml documentation as length-prefixed string) + // } + // indexPointer points to the start of the following section: + // { + // int hashcode + // int index (index where the docu string starts in the file) + // } + + void Save(string fileName, DateTime fileDate) + { + using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None)) { + using (BinaryWriter w = new BinaryWriter(fs)) { + w.Write(magic); + w.Write(version); + w.Write(fileDate.Ticks); + + IndexEntry[] index = new IndexEntry[xmlDescription.Count]; + w.Write(index.Length); + + int indexPointerPos = (int)fs.Position; + w.Write(0); // skip 4 bytes + + int i = 0; + foreach (KeyValuePair p in xmlDescription) { + index[i] = new IndexEntry(p.Key.GetHashCode(), (int)fs.Position); + w.Write(p.Key); + w.Write(p.Value.Trim()); + i += 1; + } + + Array.Sort(index); + + int indexStart = (int)fs.Position; + foreach (IndexEntry entry in index) { + w.Write(entry.HashCode); + w.Write(entry.FileLocation); + } + w.Seek(indexPointerPos, SeekOrigin.Begin); + w.Write(indexStart); + } + } + } + #endregion + + #region Load binary files + BinaryReader loader; + FileStream fs; + + bool LoadFromBinary(string fileName, DateTime fileDate) + { + keyCacheQueue = new Queue(cacheLength); + fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete); + int len = (int)fs.Length; + loader = new BinaryReader(fs); + try { + if (loader.ReadInt64() != magic) { + LoggingService.Warn("Cannot load XmlDoc: wrong magic"); + return false; + } + if (loader.ReadInt16() != version) { + LoggingService.Warn("Cannot load XmlDoc: wrong version"); + return false; + } + if (loader.ReadInt64() != fileDate.Ticks) { + LoggingService.Info("Not loading XmlDoc: file changed since cache was created"); + return false; + } + int count = loader.ReadInt32(); + int indexStartPosition = loader.ReadInt32(); // go to start of index + if (indexStartPosition <= 0 || indexStartPosition >= len) { + LoggingService.Error("XmlDoc: Cannot find index, cache invalid!"); + return false; + } + fs.Position = indexStartPosition; + IndexEntry[] index = new IndexEntry[count]; + for (int i = 0; i < index.Length; i++) { + index[i] = new IndexEntry(loader.ReadInt32(), loader.ReadInt32()); + } + this.index = index; + return true; + } catch (Exception ex) { + LoggingService.Error("Cannot load from cache", ex); + return false; + } + } + + string LoadDocumentation(string key) + { + if (keyCacheQueue.Count > cacheLength - 1) { + xmlDescription.Remove(keyCacheQueue.Dequeue()); + } + + int hashcode = key.GetHashCode(); + + // use interpolation search to find the item + string resultDocu = null; + + int m = Array.BinarySearch(index, new IndexEntry(hashcode, 0)); + if (m >= 0) { + // 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); + // go through all items that have the correct hash + while (++m < index.Length && index[m].HashCode == hashcode) { + fs.Position = index[m].FileLocation; + string keyInFile = loader.ReadString(); + if (keyInFile == key) { + //LoggingService.Debug("Got XML documentation for " + key); + resultDocu = loader.ReadString(); + break; + } else { + // this is a harmless hash collision, just continue reading + LoggingService.Warn("Found " + keyInFile + " instead of " + key); + } + } + } + + keyCacheQueue.Enqueue(key); + xmlDescription.Add(key, resultDocu); + + return resultDocu; + } + + public void Dispose() + { + if (loader != null) { + loader.Close(); + fs.Close(); + } + xmlDescription = null; + index = null; + keyCacheQueue = null; + loader = null; + fs = null; + } + #endregion + + public static XmlDoc Load(XmlReader reader) + { + XmlDoc newXmlDoc = new XmlDoc(); + while (reader.Read()) { + if (reader.IsStartElement()) { + switch (reader.LocalName) { + case "members": + newXmlDoc.ReadMembersSection(reader); + break; + } + } + } + return newXmlDoc; + } + + public static XmlDoc Load(string fileName, string cachePath) + { + return Load(fileName, cachePath, true); + } + + static XmlDoc Load(string fileName, string cachePath, bool allowRedirect) + { + LoggingService.Debug("Loading XmlDoc for " + fileName); + XmlDoc doc; + string cacheName = null; + if (cachePath != null) { + Directory.CreateDirectory(cachePath); + cacheName = cachePath + "/" + Path.GetFileNameWithoutExtension(fileName) + + "." + fileName.GetHashCode().ToString("x") + ".dat"; + if (File.Exists(cacheName)) { + doc = new XmlDoc(); + if (doc.LoadFromBinary(cacheName, File.GetLastWriteTimeUtc(fileName))) { + //LoggingService.Debug("XmlDoc: Load from cache successful"); + return doc; + } else { + doc.Dispose(); + try { + File.Delete(cacheName); + } catch {} + } + } + } + + try { + using (XmlTextReader xmlReader = new XmlTextReader(fileName)) { + xmlReader.MoveToContent(); + if (allowRedirect && !string.IsNullOrEmpty(xmlReader.GetAttribute("redirect"))) { + string redirectionTarget = GetRedirectionTarget(xmlReader.GetAttribute("redirect")); + if (redirectionTarget != null) { + LoggingService.Info("XmlDoc " + fileName + " is redirecting to " + redirectionTarget); + return Load(redirectionTarget, cachePath, false); + } else { + LoggingService.Warn("XmlDoc " + fileName + " is redirecting to " + xmlReader.GetAttribute("redirect") + ", but that file was not found."); + return new XmlDoc(); + } + } + doc = Load(xmlReader); + } + } catch (XmlException ex) { + LoggingService.Warn("Error loading XmlDoc " + fileName, ex); + return new XmlDoc(); + } + + if (cachePath != null && doc.xmlDescription.Count > cacheLength * 2) { + LoggingService.Debug("XmlDoc: Creating cache for " + fileName); + DateTime date = File.GetLastWriteTimeUtc(fileName); + try { + doc.Save(cacheName, date); + } catch (Exception ex) { + LoggingService.Error("Cannot write to cache file " + cacheName, ex); + return doc; + } + doc.Dispose(); + doc = new XmlDoc(); + doc.LoadFromBinary(cacheName, date); + } + return doc; + } + + static string GetRedirectionTarget(string target) + { + string programFilesDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + if (!programFilesDir.EndsWith("\\") && !programFilesDir.EndsWith("/")) + programFilesDir += "\\"; + + string corSysDir = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory(); + if (!corSysDir.EndsWith("\\") && !corSysDir.EndsWith("/")) + corSysDir += "\\"; + + return LookupLocalizedXmlDoc(target.Replace("%PROGRAMFILESDIR%", programFilesDir) + .Replace("%CORSYSDIR%", corSysDir)); + } + + internal static string LookupLocalizedXmlDoc(string fileName) + { + string xmlFileName = Path.ChangeExtension(fileName, ".xml"); + string currentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName; + string localizedXmlDocFile = GetLocalizedName(xmlFileName, currentCulture); + + LoggingService.Debug("Try find XMLDoc @" + localizedXmlDocFile); + if (File.Exists(localizedXmlDocFile)) { + return localizedXmlDocFile; + } + LoggingService.Debug("Try find XMLDoc @" + xmlFileName); + if (File.Exists(xmlFileName)) { + return xmlFileName; + } + if (currentCulture != "en") { + string englishXmlDocFile = GetLocalizedName(xmlFileName, "en"); + LoggingService.Debug("Try find XMLDoc @" + englishXmlDocFile); + if (File.Exists(englishXmlDocFile)) { + return englishXmlDocFile; + } + } + return null; + } + + static string GetLocalizedName(string fileName, string language) + { + string localizedXmlDocFile = Path.GetDirectoryName(fileName); + localizedXmlDocFile = Path.Combine(localizedXmlDocFile, language); + localizedXmlDocFile = Path.Combine(localizedXmlDocFile, Path.GetFileName(fileName)); + return localizedXmlDocFile; + } + } +} diff --git a/Libraries/log4net/LICENSE.txt b/Libraries/log4net/LICENSE.txt new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/Libraries/log4net/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Libraries/log4net/log4net.dll b/Libraries/log4net/log4net.dll new file mode 100644 index 0000000000000000000000000000000000000000..ffc57e11254ad9530867b35d15aaf38eb4747d8d GIT binary patch literal 270336 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