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 000000000..ffc57e112 Binary files /dev/null and b/Libraries/log4net/log4net.dll differ