From 995de1838de57b4a4726b4d91ff027059b2df6f5 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 11 May 2005 15:47:35 +0000 Subject: [PATCH] Even more performance improvements for the LoadSolutionProjectsThread. Fixed SkipCurrentBlock() in C# lexer. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@141 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/Src/Parser/Parser.cs | 2 +- .../Project/Src/Lexer/AbstractLexer.cs | 3 + .../Project/Src/Lexer/CSharp/Lexer.cs | 6 +- .../NRefactory/Project/Src/Lexer/ILexer.cs | 3 + .../Project/Src/Parser/AbstractParser.cs | 2 +- .../NRefactory/Project/Src/Parser/IParser.cs | 4 ++ .../NRefactory/Test/Lexer/CSharp/LexerTest.cs | 20 ++++-- .../NRefactory/Test/NRefactoryTests.csproj | 1 + .../NRefactory/Test/Parser/ParseUtilCSharp.cs | 6 ++ .../Test/Parser/SkipMethodBodiesTest.cs | 71 +++++++++++++++++++ .../Project/Src/Project/Items/ProjectItem.cs | 9 ++- .../CaseSensitiveProjectContent.cs | 44 ++++++++++-- .../Services/ParserService/ParserService.cs | 37 +++++----- 13 files changed, 176 insertions(+), 32 deletions(-) create mode 100644 src/Libraries/NRefactory/Test/Parser/SkipMethodBodiesTest.cs diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/Parser.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/Parser.cs index 7033d5598e..7b428e9790 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/Parser.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/Parser.cs @@ -95,7 +95,7 @@ namespace CSharpBinding.Parser ICompilationUnit Parse(ICSharpCode.NRefactory.Parser.IParser p, string fileName, IProjectContent projectContent) { p.Lexer.SpecialCommentTags = lexerTags; - ((ICSharpCode.NRefactory.Parser.AbstractParser)p).ParseMethodContents = false; + p.ParseMethodBodies = false; p.Parse(); NRefactoryASTConvertVisitor visitor = new NRefactoryASTConvertVisitor(projectContent); diff --git a/src/Libraries/NRefactory/Project/Src/Lexer/AbstractLexer.cs b/src/Libraries/NRefactory/Project/Src/Lexer/AbstractLexer.cs index 961cc7cdd9..a3eea8f723 100644 --- a/src/Libraries/NRefactory/Project/Src/Lexer/AbstractLexer.cs +++ b/src/Libraries/NRefactory/Project/Src/Lexer/AbstractLexer.cs @@ -171,6 +171,9 @@ namespace ICSharpCode.NRefactory.Parser /// /// Skips to the end of the current code block. + /// For this, the lexer must have read the next token AFTER the token opening the + /// block (so that Lexer.Token is the block-opening token, not Lexer.LookAhead). + /// After the call, Lexer.LookAhead will be the block-closing token. /// public virtual void SkipCurrentBlock() { diff --git a/src/Libraries/NRefactory/Project/Src/Lexer/CSharp/Lexer.cs b/src/Libraries/NRefactory/Project/Src/Lexer/CSharp/Lexer.cs index 68adba8a15..78e53d48e8 100644 --- a/src/Libraries/NRefactory/Project/Src/Lexer/CSharp/Lexer.cs +++ b/src/Libraries/NRefactory/Project/Src/Lexer/CSharp/Lexer.cs @@ -805,13 +805,15 @@ namespace ICSharpCode.NRefactory.Parser.CSharp /// /// Skips to the end of the current code block. + /// For this, the lexer must have read the next token AFTER the token opening the + /// block (so that Lexer.Token is the block-opening token, not Lexer.LookAhead). + /// After the call, Lexer.LookAhead will be the block-closing token. /// public override void SkipCurrentBlock() { int braceCount = 0; Token t; - StartPeek(); - while ((t = Peek()).kind != Tokens.EOF) { + while ((t = LookAhead).kind != Tokens.EOF) { if (t.kind == Tokens.OpenCurlyBrace) { ++braceCount; } else if (t.kind == Tokens.CloseCurlyBrace) { diff --git a/src/Libraries/NRefactory/Project/Src/Lexer/ILexer.cs b/src/Libraries/NRefactory/Project/Src/Lexer/ILexer.cs index e074842173..d76e128c82 100644 --- a/src/Libraries/NRefactory/Project/Src/Lexer/ILexer.cs +++ b/src/Libraries/NRefactory/Project/Src/Lexer/ILexer.cs @@ -70,6 +70,9 @@ namespace ICSharpCode.NRefactory.Parser /// /// Skips to the end of the current code block. + /// For this, the lexer must have read the next token AFTER the token opening the + /// block (so that Lexer.Token is the block-opening token, not Lexer.LookAhead). + /// After the call, Lexer.LookAhead will be the block-closing token. /// void SkipCurrentBlock(); } diff --git a/src/Libraries/NRefactory/Project/Src/Parser/AbstractParser.cs b/src/Libraries/NRefactory/Project/Src/Parser/AbstractParser.cs index 6fc4731072..d2fbb63b28 100644 --- a/src/Libraries/NRefactory/Project/Src/Parser/AbstractParser.cs +++ b/src/Libraries/NRefactory/Project/Src/Parser/AbstractParser.cs @@ -29,7 +29,7 @@ namespace ICSharpCode.NRefactory.Parser protected bool parseMethodContents = true; - public bool ParseMethodContents { + public bool ParseMethodBodies { get { return parseMethodContents; } diff --git a/src/Libraries/NRefactory/Project/Src/Parser/IParser.cs b/src/Libraries/NRefactory/Project/Src/Parser/IParser.cs index 436b5d2148..e1d45bcc97 100644 --- a/src/Libraries/NRefactory/Project/Src/Parser/IParser.cs +++ b/src/Libraries/NRefactory/Project/Src/Parser/IParser.cs @@ -29,6 +29,10 @@ namespace ICSharpCode.NRefactory.Parser get; } + bool ParseMethodBodies { + get; set; + } + void Parse(); Expression ParseExpression(); diff --git a/src/Libraries/NRefactory/Test/Lexer/CSharp/LexerTest.cs b/src/Libraries/NRefactory/Test/Lexer/CSharp/LexerTest.cs index 5e02318e8c..e475f36278 100644 --- a/src/Libraries/NRefactory/Test/Lexer/CSharp/LexerTest.cs +++ b/src/Libraries/NRefactory/Test/Lexer/CSharp/LexerTest.cs @@ -116,29 +116,37 @@ namespace ICSharpCode.NRefactory.Tests.Lexer.CSharp [Test] public void TestEmptyBlock() { - ILexer lexer = GenerateLexer(new StringReader("{}")); + ILexer lexer = GenerateLexer(new StringReader("{}+")); Assert.AreEqual(Tokens.OpenCurlyBrace, lexer.NextToken().kind); Assert.AreEqual(Tokens.CloseCurlyBrace, lexer.NextToken().kind); + Assert.AreEqual(Tokens.Plus, lexer.NextToken().kind); + Assert.AreEqual(Tokens.EOF, lexer.NextToken().kind); } [Test] public void TestSkippedEmptyBlock() { - ILexer lexer = GenerateLexer(new StringReader("{}")); + ILexer lexer = GenerateLexer(new StringReader("{}+")); Assert.AreEqual(Tokens.OpenCurlyBrace, lexer.NextToken().kind); + lexer.NextToken(); lexer.SkipCurrentBlock(); - Assert.AreEqual(Tokens.CloseCurlyBrace, lexer.NextToken().kind); + Assert.AreEqual(Tokens.CloseCurlyBrace, lexer.LookAhead.kind); + Assert.AreEqual(Tokens.Plus, lexer.NextToken().kind); + Assert.AreEqual(Tokens.EOF, lexer.NextToken().kind); } [Test] public void TestSkippedNonEmptyBlock() { - ILexer lexer = GenerateLexer(new StringReader("{ TestMethod('}'); /* }}} */ break; }")); + ILexer lexer = GenerateLexer(new StringReader("{ TestMethod('}'); /* }}} */ while(1) {break;} }+")); Assert.AreEqual(Tokens.OpenCurlyBrace, lexer.NextToken().kind); + lexer.NextToken(); lexer.SkipCurrentBlock(); - Assert.AreEqual(Tokens.CloseCurlyBrace, lexer.NextToken().kind); + Assert.AreEqual(Tokens.CloseCurlyBrace, lexer.LookAhead.kind); + Assert.AreEqual(Tokens.Plus, lexer.NextToken().kind); + Assert.AreEqual(Tokens.EOF, lexer.NextToken().kind); } - + [Test] public void TestOpenSquareBracket() { diff --git a/src/Libraries/NRefactory/Test/NRefactoryTests.csproj b/src/Libraries/NRefactory/Test/NRefactoryTests.csproj index 2c1c80a1b5..eeb7979b86 100644 --- a/src/Libraries/NRefactory/Test/NRefactoryTests.csproj +++ b/src/Libraries/NRefactory/Test/NRefactoryTests.csproj @@ -122,6 +122,7 @@ + diff --git a/src/Libraries/NRefactory/Test/Parser/ParseUtilCSharp.cs b/src/Libraries/NRefactory/Test/Parser/ParseUtilCSharp.cs index 7939443d68..d13f28c477 100644 --- a/src/Libraries/NRefactory/Test/Parser/ParseUtilCSharp.cs +++ b/src/Libraries/NRefactory/Test/Parser/ParseUtilCSharp.cs @@ -26,8 +26,14 @@ namespace ICSharpCode.NRefactory.Tests.AST } public static object ParseGlobal(string program, Type type, bool expectError) + { + return ParseGlobal(program, type, expectError, false); + } + + public static object ParseGlobal(string program, Type type, bool expectError, bool skipMethodBodies) { IParser parser = ParserFactory.CreateParser(SupportedLanguages.CSharp, new StringReader(program)); + parser.ParseMethodBodies = !skipMethodBodies; parser.Parse(); Assert.IsNotNull(parser.Errors); if (expectError) diff --git a/src/Libraries/NRefactory/Test/Parser/SkipMethodBodiesTest.cs b/src/Libraries/NRefactory/Test/Parser/SkipMethodBodiesTest.cs new file mode 100644 index 0000000000..67c7f2e321 --- /dev/null +++ b/src/Libraries/NRefactory/Test/Parser/SkipMethodBodiesTest.cs @@ -0,0 +1,71 @@ +/* + * Created by SharpDevelop. + * User: Daniel Grunwald + * Date: 11.05.2005 + * Time: 16:37 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +using System; +using System.Drawing; +using System.IO; + +using NUnit.Framework; + +using ICSharpCode.NRefactory.Parser; +using ICSharpCode.NRefactory.Parser.AST; + +namespace ICSharpCode.NRefactory.Tests.AST +{ + [TestFixture] + public class SkipMethodBodiesTest + { + [Test] + public void EmptyMethods() + { + string txt = @"internal sealed class Lexer : AbstractLexer + { + public Lexer(TextReader reader) : base(reader) + { + } + + void Method() + { + } + }"; + Check((TypeDeclaration)ParseUtilCSharp.ParseGlobal(txt, typeof(TypeDeclaration), false, true)); + } + + [Test] + public void NonEmptyMethods() + { + string txt = @"internal sealed class Lexer : AbstractLexer + { + public Lexer(TextReader reader) : base(reader) + { + if (reader == null) { + throw new ArgumentNullException(""reader""); + } + } + + void Method() + { + while(something) { + if (anything) + break; + } + } + }"; + Check((TypeDeclaration)ParseUtilCSharp.ParseGlobal(txt, typeof(TypeDeclaration), false, true)); + } + + void Check(TypeDeclaration td) + { + Assert.AreEqual("Lexer", td.Name); + Assert.AreEqual(2, td.Children.Count); + Assert.AreEqual(0, ((ConstructorDeclaration)td.Children[0]).Body.Children.Count); + Assert.AreEqual(0, ((MethodDeclaration)td.Children[1]).Body.Children.Count); + } + } +} diff --git a/src/Main/Base/Project/Src/Project/Items/ProjectItem.cs b/src/Main/Base/Project/Src/Project/Items/ProjectItem.cs index 35b87afb4b..9e19b36b4e 100644 --- a/src/Main/Base/Project/Src/Project/Items/ProjectItem.cs +++ b/src/Main/Base/Project/Src/Project/Items/ProjectItem.cs @@ -47,6 +47,7 @@ namespace ICSharpCode.SharpDevelop.Project } set { project = value; + fileNameCache = null; } } @@ -57,6 +58,7 @@ namespace ICSharpCode.SharpDevelop.Project } set { include = value; + fileNameCache = null; } } @@ -67,12 +69,17 @@ namespace ICSharpCode.SharpDevelop.Project } } + string fileNameCache; + [Browsable(false)] public virtual string FileName { get { - return Path.Combine(project.Directory, Include); + if (fileNameCache == null) + fileNameCache = Path.Combine(project.Directory, include); + return fileNameCache; } set { + fileNameCache = null; Include = FileUtility.GetRelativePath(project.Directory, value); } } diff --git a/src/Main/Base/Project/Src/Services/ParserService/CaseSensitiveProjectContent.cs b/src/Main/Base/Project/Src/Services/ParserService/CaseSensitiveProjectContent.cs index cc9218eee7..4ae1d1fa6d 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/CaseSensitiveProjectContent.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/CaseSensitiveProjectContent.cs @@ -203,27 +203,61 @@ namespace ICSharpCode.Core new AddReferenceDelegate(AddReference).BeginInvoke(e.ReferenceProjectItem, null, null); } + delegate string GetParseableContentDelegate(string fileName); + + Encoding getParseableContentEncoding; + + string GetParseableFileContent(string fileName) + { + // Loading the source files is done asynchronously: + // While one file is parsed, the next is already loaded from disk. + string res = project.GetParseableFileContent(fileName); + if (res != null) + return res; + // load file + using (StreamReader r = new StreamReader(fileName, getParseableContentEncoding)) { + return r.ReadToEnd(); + } + } + internal void Initialize2() { if (!initializing) return; ProjectItem[] arr = project.Items.ToArray(); try { + Properties textEditorProperties = ((Properties)PropertyService.Get("ICSharpCode.TextEditor.Document.Document.DefaultDocumentAggregatorProperties", new Properties())); + getParseableContentEncoding = Encoding.GetEncoding(textEditorProperties.Get("Encoding", 1252)); + textEditorProperties = null; + StatusBarService.ProgressMonitor.BeginTask("Parsing " + project.Name + "...", arr.Length); + GetParseableContentDelegate pcd = new GetParseableContentDelegate(GetParseableFileContent); + ProjectItem item; + ProjectItem nextItem = arr[0]; + IAsyncResult res = null; for (int i = 0; i < arr.Length; ++i) { - ProjectItem item = arr[i]; + item = nextItem; + nextItem = (i < arr.Length - 1) ? arr[i + 1] : null; if ((i % 5) == 2) StatusBarService.ProgressMonitor.WorkDone = i; if (item.ItemType == ItemType.Compile) { - ParseInformation parseInfo = ParserService.ParseFile(item.FileName, null, true, false); - if (parseInfo != null) { - UpdateCompilationUnit(null, parseInfo.BestCompilationUnit as ICompilationUnit, item.FileName, true); - } + string fileName = item.FileName; + string fileContent; + if (res != null) + fileContent = pcd.EndInvoke(res); + else + fileContent = GetParseableFileContent(fileName); + if (nextItem != null && nextItem.ItemType == ItemType.Compile) + res = pcd.BeginInvoke(nextItem.FileName, null, null); + else + res = null; + ParserService.ParseFile(this, fileName, fileContent, true, false); } if (!initializing) return; } } finally { StatusBarService.ProgressMonitor.Done(); initializing = false; + getParseableContentEncoding = null; } } diff --git a/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs b/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs index 69396d6040..98c30676eb 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs @@ -251,9 +251,11 @@ namespace ICSharpCode.Core static IProjectContent GetProjectContent(string fileName) { - foreach (KeyValuePair projectContent in projectContents) { - if (projectContent.Key.IsFileInProject(fileName)) { - return projectContent.Value; + lock (projectContents) { + foreach (KeyValuePair projectContent in projectContents) { + if (projectContent.Key.IsFileInProject(fileName)) { + return projectContent.Value; + } } } return null; @@ -262,6 +264,11 @@ namespace ICSharpCode.Core static IProjectContent defaultProjectContent = new DefaultProjectContent(); public static ParseInformation ParseFile(string fileName, string fileContent, bool updateCommentTags, bool fireUpdate) + { + return ParseFile(null, fileName, fileContent, updateCommentTags, fireUpdate); + } + + public static ParseInformation ParseFile(IProjectContent fileProjectContent, string fileName, string fileContent, bool updateCommentTags, bool fireUpdate) { IParser parser = GetParser(fileName); if (parser == null) { @@ -281,9 +288,13 @@ namespace ICSharpCode.Core } } try { - IProjectContent fileProjectContent = GetProjectContent(fileName); if (fileProjectContent == null) { - fileProjectContent = defaultProjectContent; + // GetProjectContent is expensive because it compares all file names, so + // we accept the project content as optional parameter. + fileProjectContent = GetProjectContent(fileName); + if (fileProjectContent == null) { + fileProjectContent = defaultProjectContent; + } } if (fileContent != null) { @@ -295,17 +306,11 @@ namespace ICSharpCode.Core parserOutput = parser.Parse(fileProjectContent, fileName); } - lock (projectContents) { - foreach (KeyValuePair projectContent in projectContents) { - if (projectContent.Key.IsFileInProject(fileName)) { - if (parsings.ContainsKey(fileName)) { - ParseInformation parseInformation = parsings[fileName]; - projectContent.Value.UpdateCompilationUnit(parseInformation.MostRecentCompilationUnit, parserOutput as ICompilationUnit, fileName, updateCommentTags); - } else { - projectContent.Value.UpdateCompilationUnit(null, parserOutput, fileName, updateCommentTags); - } - } - } + if (parsings.ContainsKey(fileName)) { + ParseInformation parseInformation = parsings[fileName]; + fileProjectContent.UpdateCompilationUnit(parseInformation.MostRecentCompilationUnit, parserOutput as ICompilationUnit, fileName, updateCommentTags); + } else { + fileProjectContent.UpdateCompilationUnit(null, parserOutput, fileName, updateCommentTags); } return UpdateParseInformation(parserOutput as ICompilationUnit, fileName, updateCommentTags, fireUpdate); } catch (Exception e) {