From 4e3b346a8f0d89b1f03554833485fd229c4fea9c Mon Sep 17 00:00:00 2001 From: osmedile Date: Sat, 8 Dec 2018 13:50:12 +0100 Subject: [PATCH 01/11] WI #1301 SymbolTable: Remove unused methods and code --- TypeCobol/Compiler/CodeModel/SymbolTable.cs | 50 +++------------------ 1 file changed, 6 insertions(+), 44 deletions(-) diff --git a/TypeCobol/Compiler/CodeModel/SymbolTable.cs b/TypeCobol/Compiler/CodeModel/SymbolTable.cs index f7c9b1087..9f0ef4c25 100644 --- a/TypeCobol/Compiler/CodeModel/SymbolTable.cs +++ b/TypeCobol/Compiler/CodeModel/SymbolTable.cs @@ -307,9 +307,7 @@ public DataDefinition GetRedefinedVariable(DataRedefines redefinesNode, SymbolRe var childrens = redefinesNode.Parent.Children; int index = redefinesNode.Parent.IndexOf(redefinesNode); - bool redefinedVariableFound = false; - - while (!redefinedVariableFound && index >= 0) + while (index >= 0) { CommonDataDescriptionAndDataRedefines child = childrens[index].CodeElement as CommonDataDescriptionAndDataRedefines; @@ -974,10 +972,7 @@ public List GetFunction(StorageArea storageArea, ParameterL return GetFunction(storageArea.SymbolReference, profile); } - public List GetFunction(VariableBase variable, ParameterList profile = null) - { - return GetFunction(new URI(variable.ToString()), profile); - } + public List GetFunction(SymbolReference symbolReference, ParameterList profile = null) { @@ -1124,43 +1119,10 @@ private IDictionary> GetFunctionTable(SymbolTa public void AddProgram(Program program) { Add(Programs, program); - } - - /// - /// Add Multiple programs to SymbolTable - /// - /// - public void AddPrograms(List programs) - { - foreach (var program in programs) - { - AddProgram(program); - } - } - - public List GetProgram(StorageArea storageArea, ParameterList profile = null) - { - return GetProgram(storageArea.SymbolReference, profile); - } - - public List GetProgram(VariableBase variable, ParameterList profile = null) - { - return GetProgram(new URI(variable.ToString()), profile); - } - - public List GetProgram(SymbolReference symbolReference, ParameterList profile = null) - { - var uri = new URI(symbolReference.Name); - return GetProgram(uri, profile); - } - - public List GetProgram(QualifiedName name, ParameterList profile = null) - { - var found = GetProgram(name.Head); - found = Get(found, name); - - return found; - } + } + + + [NotNull] private List GetProgram(string name) From 6c808fed58808fa42e85a39c406640a36f5a8e90 Mon Sep 17 00:00:00 2001 From: osmedile Date: Tue, 4 Dec 2018 23:11:51 +0100 Subject: [PATCH 02/11] WI #1301 SymbolTable: Limit search of symbol to a max scope Search for paragraphs/sections are limited to current SymbolTable. Search for variables are limited to scope GlobalStorage. Search for functions/types in all scopes. --- TypeCobol/Compiler/CodeModel/SymbolTable.cs | 36 +++++++++++++-------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/TypeCobol/Compiler/CodeModel/SymbolTable.cs b/TypeCobol/Compiler/CodeModel/SymbolTable.cs index 9f0ef4c25..ac9684e31 100644 --- a/TypeCobol/Compiler/CodeModel/SymbolTable.cs +++ b/TypeCobol/Compiler/CodeModel/SymbolTable.cs @@ -10,6 +10,7 @@ using System.Linq.Expressions; using System.Text.RegularExpressions; using Castle.Core.Internal; +using TypeCobol.Compiler.Concurrency; namespace TypeCobol.Compiler.CodeModel { @@ -82,14 +83,13 @@ public SymbolTable(SymbolTable enclosing, Scope current) } private List GetFromTableAndEnclosing(string head, - Func>> getTableFunction, SymbolTable symbolTable = null) where T : Node + Func>> getTableFunction, Scope maxScope = Scope.Intrinsic) where T : Node { - symbolTable = symbolTable ?? this; - var table = getTableFunction.Invoke(symbolTable); + var table = getTableFunction.Invoke(this); var values = GetFromTable(head, table); - if (EnclosingScope != null) + if (EnclosingScope != null && EnclosingScope.CurrentScope >= maxScope) { - values.AddRange(EnclosingScope.GetFromTableAndEnclosing(head, getTableFunction)); + values.AddRange(EnclosingScope.GetFromTableAndEnclosing(head, getTableFunction, maxScope)); } return values; } @@ -194,7 +194,7 @@ public IEnumerable GetVariables(SymbolReference symbolReference) private IList GetVariables(string name) { //Try to get variable in the current program - var found = GetFromTableAndEnclosing(name, GetDataDefinitionTable); + var found = GetFromTableAndEnclosing(name, GetDataDefinitionTable, Scope.GlobalStorage); return found; } @@ -512,12 +512,12 @@ public void MatchVariable(IList found, DataDefinition headDataDe var dataType = GetAllEnclosingTypeReferences().FirstOrDefault(k => k.Key == currentTypeDef); //Let's get typereferences (built by TypeCobolLinker phase) if (dataType.Key == null || dataType.Value == null) return; - var references = dataType.Value; + IEnumerable references = dataType.Value; //If typedefcontext is set : Ignore references of this typedefContext to avoid loop seeking // Only takes variable references that are declared inside the typeDefContext if (typeDefContext != null) - references = references.Where(r => r.DataType != typeDefContext.DataType && r.ParentTypeDefinition == typeDefContext).ToList(); + references = references.Where(r => r.DataType != typeDefContext.DataType && r.ParentTypeDefinition == typeDefContext); var primaryPath = completeQualifiedNames.Last().ToArray(); //PrmiaryPath that will be added in front of every reference's path found foreach (var reference in references) @@ -557,11 +557,11 @@ private void AddAllReference(IList found, DataDefinition heaData var dataType = GetAllEnclosingTypeReferences().FirstOrDefault(k => k.Key == currentDataDefinition); if (dataType.Key == null || dataType.Value == null) return; - var references = dataType.Value; + IEnumerable references = dataType.Value; //If typedefcontext is setted : Ignore references of this typedefContext to avoid loop seeking // + Only takes variable references that are declared inside the typeDefContext if (typeDefContext != null) - references = references.Where(r => r.DataType != typeDefContext.DataType && r.ParentTypeDefinition == typeDefContext).ToList(); + references = references.Where(r => r.DataType != typeDefContext.DataType && r.ParentTypeDefinition == typeDefContext); var typePath = completeQualifiedNames.Last().ToArray(); var referenceCounter = 0; foreach (var reference in references) @@ -741,6 +741,8 @@ private Node GetAncestor(Node node, int generation) private IDictionary> Sections = new Dictionary>(StringComparer.OrdinalIgnoreCase); + private static IList
EmptySectionList = new ImmutableList
(); + internal void AddSection(Section section) { Add(Sections, section); @@ -748,7 +750,10 @@ internal void AddSection(Section section) public IList
GetSection(string name) { - return GetFromTableAndEnclosing(name, GetSectionTable); + Sections.TryGetValue(name, out var values); + if (values != null) return values.ToList(); //.ToList so the caller cannot modify our stored list + + return EmptySectionList; } private IDictionary> GetSectionTable(SymbolTable symbolTable) @@ -763,6 +768,8 @@ private IDictionary> GetSectionTable(SymbolTable symbolTab private IDictionary> Paragraphs = new Dictionary>(StringComparer.OrdinalIgnoreCase); + private static IList EmptyParagraphList = new ImmutableList(); + internal void AddParagraph(Paragraph paragraph) { Add(Paragraphs, paragraph); @@ -770,7 +777,10 @@ internal void AddParagraph(Paragraph paragraph) public IList GetParagraph(string name) { - return GetFromTableAndEnclosing(name, GetParagraphTable); + Paragraphs.TryGetValue(name, out var values); + if (values != null) return values.ToList(); //.ToList so the caller cannot modify our stored list + + return EmptyParagraphList; } private IDictionary> GetParagraphTable(SymbolTable symbolTable) @@ -1127,7 +1137,7 @@ public void AddProgram(Program program) [NotNull] private List GetProgram(string name) { - return GetFromTableAndEnclosing(name, GetProgramsTable); + return GetFromTableAndEnclosing(name, GetProgramsTable, Scope.Namespace); } private IDictionary> GetProgramsTable(SymbolTable symbolTable) From 6a04638ff5cfbfb4a7288db9c7b0c9f434fe32bc Mon Sep 17 00:00:00 2001 From: osmedile Date: Tue, 4 Dec 2018 23:12:59 +0100 Subject: [PATCH 03/11] WI #1294 Avoid multiple enumeration of an IEnumerable Put Count of IEnumerable on a temporary var to avoid multiple Count --- TypeCobol/Compiler/Diagnostics/TypeCobolChecker.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/TypeCobol/Compiler/Diagnostics/TypeCobolChecker.cs b/TypeCobol/Compiler/Diagnostics/TypeCobolChecker.cs index 2049e4538..90fcf6d7b 100644 --- a/TypeCobol/Compiler/Diagnostics/TypeCobolChecker.cs +++ b/TypeCobol/Compiler/Diagnostics/TypeCobolChecker.cs @@ -147,7 +147,8 @@ public static void OnNode(Node node) var potentialVariables = node.SymbolTable.GetVariablesExplicit(new URI(functionCaller.FunctionCall.FunctionName)); - if (functionDeclarations.Count == 1 && !potentialVariables.Any()) + var potentialVariablesCount = potentialVariables.Count(); + if (functionDeclarations.Count == 1 && potentialVariablesCount == 0) { functionCaller.FunctionDeclaration = functionDeclarations.First(); return; //Everything seems to be ok, lets continue on the next one @@ -157,7 +158,7 @@ public static void OnNode(Node node) node.SymbolTable.GetFunction(new URI(functionCaller.FunctionCall.FunctionName), null, functionCaller.FunctionCall.Namespace); - if (potentialVariables.Count() > 1) + if (potentialVariablesCount > 1) { //If there is more than one variable with the same name, it's ambiguous message = string.Format("Call to '{0}'(no arguments) is ambigous. '{0}' is defined {1} times", @@ -167,7 +168,7 @@ public static void OnNode(Node node) return; } - if (functionDeclarations.Count > 1 && !potentialVariables.Any()) + if (functionDeclarations.Count > 1 && potentialVariablesCount == 0) { message = string.Format("No suitable function signature found for '{0}(no arguments)'", functionCaller.FunctionCall.FunctionName); @@ -175,7 +176,7 @@ public static void OnNode(Node node) return; } - if (functionDeclarations.Count >= 1 && potentialVariables.Count() == 1) + if (functionDeclarations.Count >= 1 && potentialVariablesCount == 1) { message = string.Format("Warning: Risk of confusion in call of '{0}'", functionCaller.FunctionCall.FunctionName); @@ -183,7 +184,7 @@ public static void OnNode(Node node) return; } - if (functionDeclarations.Count == 0 && !potentialVariables.Any()) + if (functionDeclarations.Count == 0 && potentialVariablesCount == 0) { message = string.Format("No function or variable found for '{0}'(no arguments)", functionCaller.FunctionCall.FunctionName); @@ -191,7 +192,7 @@ public static void OnNode(Node node) return; //Do not continue the function/procedure does not exists } - if (potentialVariables.Count() == 1) + if (potentialVariablesCount == 1) return; //Stop here, it's a standard Cobol call } From 15a2cb978a7b81dd73298e90fec16e4bc4dccca5 Mon Sep 17 00:00:00 2001 From: osmedile Date: Sat, 8 Dec 2018 13:31:30 +0100 Subject: [PATCH 04/11] WI #1301 Separate variable lookup for vars inside/outside typedef There are 2 separate method to seach variables: - Variable outside typedef with a simple algo - Variable inside typedef with a complex algo Code separation make it easier to understand and increase performance for code without typedef. --- TypeCobol/Compiler/CodeModel/SymbolTable.cs | 83 +++++++++++++++++---- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/TypeCobol/Compiler/CodeModel/SymbolTable.cs b/TypeCobol/Compiler/CodeModel/SymbolTable.cs index ac9684e31..858fca2d5 100644 --- a/TypeCobol/Compiler/CodeModel/SymbolTable.cs +++ b/TypeCobol/Compiler/CodeModel/SymbolTable.cs @@ -401,15 +401,14 @@ public List> GetVariablesExplicitWithQualif TypeDefinition typeDefContext = null) { List> foundedVariables = new List>(); - //Get variable name declared into typedef declaration - var candidates = GetCustomTypesSubordinatesNamed(name.Head); - //Get all variables that corresponds to the given head of QualifiedName - candidates.AddRange(GetVariables(name.Head)); - + + + #region Get variables declared under Type var found = new List(); - int foundCount = 0; var completeQualifiedNames = new List>(); - foreach (var candidate in candidates.Distinct()) + int foundCount = 0; + //Get all variables that corresponds to the given head of QualifiedName + foreach (var candidate in GetCustomTypesSubordinatesNamed(name.Head)) { completeQualifiedNames.Add(new List()); MatchVariable(found, candidate, name, name.Count - 1, candidate, completeQualifiedNames, @@ -434,9 +433,67 @@ public List> GetVariablesExplicitWithQualif if (completeQualifiedNames.Count == i) break; } + #endregion + + + #region Get variables declared outside types + //If we are in the context of a typedef, it will be handled by "Get variables declared under Type" + if (typeDefContext == null) + { + var varOutsideTypes = GetVariables(name.Head); + //Get variable name declared outside typedef declaration + foreach (var candidate in varOutsideTypes) + { + MatchVariableOutsideType(foundedVariables, candidate, name, name.Count - 1, candidate); + } + } + #endregion return foundedVariables; } + + + /// + /// Recursively try to find the path for the given QualifiedName name. + /// Algorithm allows to browse every potential path to find where the variable QualifiedName is. + /// It only browse DataDefinition (Var + Group) outside TypeDef. + /// The algorithm will search as deep as possible in every direction until the path comes to an end. + /// + /// List of compatible variable found regarding to the given name + /// Given potential variable candidate + /// QualifiedName of the symbol looked for + /// Total count of the parts of the qualifiedName 'name' + /// Currently checked DataDefinition + public void MatchVariableOutsideType(List> found, in DataDefinition headDataDefinition, in QualifiedName name, + int nameIndex, in DataDefinition currentDataDefinition) + { + + //Name match ? + if (name[nameIndex].Equals(currentDataDefinition.Name, StringComparison.OrdinalIgnoreCase)) + { + nameIndex--; + if (nameIndex < 0) + { //We reached the end of the name : it's a complete match + + //we are on a variable + found.Add(new KeyValuePair(headDataDefinition.QualifiedName.ToString(), headDataDefinition)); + + //End here + return; + } + //else it's not the end of name, let's continue with next part of QualifiedName + } + + //Either we have a match or not, we need to continue to the parent + if (currentDataDefinition.Parent is DataDefinition parent) + { + //Go deeper to check the rest of the QualifiedName 'name' + MatchVariableOutsideType(found, headDataDefinition, name, nameIndex, parent); + } + + //If we reach here, it means we are on a DataDefinition with no parent + //==> End of treatment, there is no match + } /// /// Recursively try to find the path for the given QualifiedName name. @@ -451,8 +508,8 @@ public List> GetVariablesExplicitWithQualif /// Currently checked DataDefinition /// List of list of string that allows to store all the different path for the founded possibilities /// TypeDefinition context to force the algorithm to only work inside the typedef scope - public void MatchVariable(IList found, DataDefinition headDataDefinition, QualifiedName name, - int nameIndex, DataDefinition currentDataDefinition, List> completeQualifiedNames, TypeDefinition typeDefContext) { + public void MatchVariable(IList found, in DataDefinition headDataDefinition, in QualifiedName name, + int nameIndex, in DataDefinition currentDataDefinition, List> completeQualifiedNames, TypeDefinition typeDefContext) { completeQualifiedNames.Last().Add(currentDataDefinition.Name); var currentTypeDef = currentDataDefinition as TypeDefinition; @@ -498,8 +555,7 @@ public void MatchVariable(IList found, DataDefinition headDataDe //Either we have a match or not, we need to continue to the parent or DataDefinition that use this TypeDefinition - var parent = currentDataDefinition.Parent as DataDefinition; - if (parent != null) + if (currentDataDefinition.Parent is DataDefinition parent) { //Go deeper to check the rest of the QualifiedName 'name' MatchVariable(found, headDataDefinition, name, nameIndex, parent, completeQualifiedNames, typeDefContext); @@ -514,12 +570,12 @@ public void MatchVariable(IList found, DataDefinition headDataDe return; IEnumerable references = dataType.Value; - //If typedefcontext is set : Ignore references of this typedefContext to avoid loop seeking + //If typeDefContext is set : Ignore references of this typedefContext to avoid loop seeking // Only takes variable references that are declared inside the typeDefContext if (typeDefContext != null) references = references.Where(r => r.DataType != typeDefContext.DataType && r.ParentTypeDefinition == typeDefContext); - var primaryPath = completeQualifiedNames.Last().ToArray(); //PrmiaryPath that will be added in front of every reference's path found + var primaryPath = completeQualifiedNames.Last().ToArray(); //PrimaryPath that will be added in front of every reference's path found foreach (var reference in references) { //references property of a TypeDefinition can lead to variable in totally others scopes, like in another program @@ -534,7 +590,6 @@ public void MatchVariable(IList found, DataDefinition headDataDe } } - return; } //If we reach here, it means we are on a DataDefinition with no parent From 93d5f7281c7616593935875fab219a3770106eb1 Mon Sep 17 00:00:00 2001 From: osmedile Date: Sat, 8 Dec 2018 13:51:30 +0100 Subject: [PATCH 05/11] WI #1301 GetType return empty list if dataType Cob85 GetType is intented fot find a TypeDefinition. TypeDefinition is from Cobol2002 or above so there is no need to search for a TypeDefinition if the dataType is Cobol85. --- TypeCobol/Compiler/CodeModel/SymbolTable.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/TypeCobol/Compiler/CodeModel/SymbolTable.cs b/TypeCobol/Compiler/CodeModel/SymbolTable.cs index 858fca2d5..985c33978 100644 --- a/TypeCobol/Compiler/CodeModel/SymbolTable.cs +++ b/TypeCobol/Compiler/CodeModel/SymbolTable.cs @@ -892,20 +892,28 @@ public void AddDataDefinitionsUnderType([NotNull] DataDefinition data) } } + public IList EmptyTypeDefinitionList = new List(); + [NotNull] public IList GetType(DataDefinition symbol) { return GetType(symbol.DataType); } + [NotNull] public List GetType(SymbolReference symbolReference) { return GetType(symbolReference.URI); } - - public List GetType(DataType dataType, string pgmName = null) + [NotNull] + public IList GetType(DataType dataType, string pgmName = null) { + if (dataType.CobolLanguageLevel == CobolLanguageLevel.Cobol85) + { + return EmptyTypeDefinitionList; + } + var uri = new URI(dataType.Name); var types = GetType(uri); if (types.Count > 0) From 41e293131441d45dea677f520c037a19800a48a9 Mon Sep 17 00:00:00 2001 From: osmedile Date: Sat, 8 Dec 2018 23:02:51 +0100 Subject: [PATCH 06/11] WI #1301 SymbolTable: rewrite GetAllEnclosingTypeReferences Speed up lookup of variables inside typedef. Rewrite algorithm to avoid multiple enumeration of list --- TypeCobol/Compiler/CodeModel/SymbolTable.cs | 53 ++++++++------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/TypeCobol/Compiler/CodeModel/SymbolTable.cs b/TypeCobol/Compiler/CodeModel/SymbolTable.cs index 985c33978..f81967a78 100644 --- a/TypeCobol/Compiler/CodeModel/SymbolTable.cs +++ b/TypeCobol/Compiler/CodeModel/SymbolTable.cs @@ -27,49 +27,39 @@ public class SymbolTable /// /// Allow to get all the Type's references from any Enclosing Scope or Program /// - public Dictionary> GetAllEnclosingTypeReferences() + public IEnumerable GetAllEnclosingTypeReferences(TypeDefinition currentTypeDef) { - var result = new Dictionary>(); - SymbolTable scope = this;//By default set this symboltable as the starting point + var result = new List(); + SymbolTable symbolTable = this;//By default set this symboltable as the starting point - while (scope != null) //Loop on enclosing scope until null scope. + while (symbolTable.CurrentScope >= Scope.Namespace) //Loop on enclosing scope until null scope. { - foreach (var typeReference in scope.TypesReferences.Select(pt => new KeyValuePair>(pt.Key, pt.Value.ToArray().ToList()))) //new KeyValuePair allow to loose object ref + symbolTable.TypesReferences.TryGetValue(currentTypeDef, out var typeReferences); + if (typeReferences != null) { - if (!result.ContainsKey(typeReference.Key)) //Avoid duplicate key - result.Add(typeReference.Key, typeReference.Value); + result.AddRange(typeReferences); } - if (scope.CurrentScope == Scope.Namespace && scope.Programs.Any()) + if (symbolTable.CurrentScope == Scope.Namespace && symbolTable.Programs.Count > 0) //Some TypeReferences are stored only in program's symbolTable, need to seek into them. { - foreach (var program in scope.Programs.SelectMany(t => t.Value)) + foreach (var program in symbolTable.Programs.SelectMany(t => t.Value)) { - if (program != null && program.SymbolTable != null && - !program.SymbolTable.TypesReferences.IsNullOrEmpty()) + if (!program.SymbolTable.TypesReferences.IsNullOrEmpty()) { - foreach (var progTypeRef in program.SymbolTable.TypesReferences.Select(pt => - new KeyValuePair>(pt.Key, pt.Value.ToArray().ToList()))) //new KeyValuePair allow to loose object ref + + program.SymbolTable.TypesReferences.TryGetValue(currentTypeDef, out var typeReferences2); + if (typeReferences2 != null) { - if (!result.ContainsKey(progTypeRef.Key)) //Avoid duplicate key - result.Add(progTypeRef.Key, progTypeRef.Value); - else - { - foreach (var reference in progTypeRef.Value) //Add the reference values not already discovered - { - if (!result[progTypeRef.Key].Contains(reference)) - result[progTypeRef.Key].Add(reference); - } - } - + result.AddRange(typeReferences2); } } } } - scope = scope.EnclosingScope; //Go to the next enclosing scope. + symbolTable = symbolTable.EnclosingScope; //Go to the next enclosing scope. } - return result; + return result.Distinct(); } @@ -565,10 +555,7 @@ public void MatchVariable(IList found, in DataDefinition headDat if (currentTypeDef != null) //We've found that we are currently onto a typedef. { - var dataType = GetAllEnclosingTypeReferences().FirstOrDefault(k => k.Key == currentTypeDef); //Let's get typereferences (built by TypeCobolLinker phase) - if (dataType.Key == null || dataType.Value == null) - return; - IEnumerable references = dataType.Value; + IEnumerable references = GetAllEnclosingTypeReferences(currentTypeDef); //Let's get typeReferences (built by TypeCobolLinker phase) //If typeDefContext is set : Ignore references of this typedefContext to avoid loop seeking // Only takes variable references that are declared inside the typeDefContext @@ -609,10 +596,8 @@ public void MatchVariable(IList found, in DataDefinition headDat private void AddAllReference(IList found, DataDefinition heaDataDefinition, [NotNull] TypeDefinition currentDataDefinition, List> completeQualifiedNames, TypeDefinition typeDefContext) { completeQualifiedNames.Last().Add(currentDataDefinition.Name); - var dataType = GetAllEnclosingTypeReferences().FirstOrDefault(k => k.Key == currentDataDefinition); - if (dataType.Key == null || dataType.Value == null) - return; - IEnumerable references = dataType.Value; + IEnumerable references = GetAllEnclosingTypeReferences(currentDataDefinition); + //If typedefcontext is setted : Ignore references of this typedefContext to avoid loop seeking // + Only takes variable references that are declared inside the typeDefContext if (typeDefContext != null) From cc6ad9d8ab3e0547b7c6e4e48975e88b34b1ea09 Mon Sep 17 00:00:00 2001 From: osmedile Date: Sun, 9 Dec 2018 13:04:01 +0100 Subject: [PATCH 07/11] WI #1301 SymbolTable: Remove useless ToList --- TypeCobol/Compiler/CodeModel/SymbolTable.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TypeCobol/Compiler/CodeModel/SymbolTable.cs b/TypeCobol/Compiler/CodeModel/SymbolTable.cs index f81967a78..98e4a115d 100644 --- a/TypeCobol/Compiler/CodeModel/SymbolTable.cs +++ b/TypeCobol/Compiler/CodeModel/SymbolTable.cs @@ -1200,10 +1200,10 @@ public IEnumerable GetPrograms(string filter) .Where(fd => fd.Name.StartsWith(filter, StringComparison.OrdinalIgnoreCase)); } - public List GetPrograms() + public IEnumerable GetPrograms() { return this.GetTableFromScope(Scope.Namespace) - .Programs.Values.SelectMany(t => t).ToList(); + .Programs.Values.SelectMany(t => t); } From c296c7bdf8b6f20a18050744c8c0d189787c83dc Mon Sep 17 00:00:00 2001 From: osmedile Date: Sat, 8 Dec 2018 13:51:58 +0100 Subject: [PATCH 08/11] WI #1301 GetFromTable don't create multiple list but use result list passed as arguments This avoid to create multiple list especially when there is no symbol found in the current SymbolTable --- TypeCobol/Compiler/CodeModel/SymbolTable.cs | 36 ++++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/TypeCobol/Compiler/CodeModel/SymbolTable.cs b/TypeCobol/Compiler/CodeModel/SymbolTable.cs index 98e4a115d..53c57dc91 100644 --- a/TypeCobol/Compiler/CodeModel/SymbolTable.cs +++ b/TypeCobol/Compiler/CodeModel/SymbolTable.cs @@ -72,29 +72,55 @@ public SymbolTable(SymbolTable enclosing, Scope current) throw new InvalidOperationException("Only Table of INTRINSIC symbols don't have any enclosing scope."); } + /// + /// GetFromTableAndEnclosing 2 + /// + /// + /// + /// + /// + /// + [NotNull] private List GetFromTableAndEnclosing(string head, Func>> getTableFunction, Scope maxScope = Scope.Intrinsic) where T : Node + { + System.Diagnostics.Debug.Assert(head != null); + var result = new List(); + this.GetFromTableAndEnclosing2(head, getTableFunction, result, maxScope); + + return result; + } + + private void GetFromTableAndEnclosing2([NotNull] string head, + Func>> getTableFunction, List result, Scope maxScope = Scope.Intrinsic) where T : Node { var table = getTableFunction.Invoke(this); - var values = GetFromTable(head, table); + GetFromTable(head, table, result); if (EnclosingScope != null && EnclosingScope.CurrentScope >= maxScope) { - values.AddRange(EnclosingScope.GetFromTableAndEnclosing(head, getTableFunction, maxScope)); + EnclosingScope.GetFromTableAndEnclosing2(head, getTableFunction, result, maxScope); } - return values; } + + + [NotNull] private List GetFromTable(string head, IDictionary> table) where T : Node { if (head != null) { - List values; - table.TryGetValue(head, out values); + table.TryGetValue(head, out List values); if (values != null) return values.ToList(); } return new List(); } + private void GetFromTable(string head, IDictionary> table, List result) where T : Node + { + table.TryGetValue(head, out List values); + if (values != null) result.AddRange(values); + } + #region DATA SYMBOLS /// From 637900a08f1b7fcbede1c454ad333f40819986c7 Mon Sep 17 00:00:00 2001 From: osmedile Date: Sun, 9 Dec 2018 10:34:12 +0100 Subject: [PATCH 09/11] WI #1302 Do not search for circular type when outisde typedef Do not search for type reference for DataDefinition outside typedef. --- .../CircularReferenceType.rdzMix.txt | 38 ++++++++++++++ .../CircularReferenceType.rdzPGM.txt | 6 +-- .../Compiler/Diagnostics/TypeCobolLinker.cs | 52 +++++++++++++------ 3 files changed, 76 insertions(+), 20 deletions(-) create mode 100644 TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzMix.txt diff --git a/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzMix.txt b/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzMix.txt new file mode 100644 index 000000000..1e29cd450 --- /dev/null +++ b/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzMix.txt @@ -0,0 +1,38 @@ + IDENTIFICATION DIVISION. + PROGRAM-ID. CircularRefCheck. + + DATA DIVISION. + WORKING-STORAGE SECTION. + + 01 ThirdType TYPEDEF STRICT. +Line 8[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected + 05 renjgrn TYPE myType. + + 01 myType TYPEDEF STRICT. + 05 myVar PIC X(10). + 05 secondGroup pic X. +Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected +Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected +Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected + 05 yhrtger TYPE ThirdType. +Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected +Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected +Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected + 05 ezgoerk TYPE MySendType. + + 01 MyGroup. +Line 17[15,20] <30, Error, Semantics> - Semantic error: Variable 'MyVar1' has to be limited to level 47 because of 'myType' maximum estimated children level + 48 MyVar1 TYPE myType. + 45 MyVar2 TYPE myType. + + + 01 MySendType TYPEDEF STRICT. + 05 MyVariable PIC X(10). + 05 MySecVariable PIC X. +Line 24[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected + 05 SelfRef TYPE myType. + + + PROCEDURE DIVISION. + move MyVar1::myVar to MyVar2::secondGroup. + END PROGRAM CircularRefCheck. diff --git a/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzPGM.txt b/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzPGM.txt index bc2b0c41b..924ceff2f 100644 --- a/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzPGM.txt +++ b/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzPGM.txt @@ -1,12 +1,8 @@ ---- Diagnostics --- +--- Diagnostics --- Line 8[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:renjgrn] Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:yhrtger] Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:yhrtger] Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:yhrtger] -Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:yhrtger] -Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:yhrtger] -Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:ezgoerk] -Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:ezgoerk] Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:ezgoerk] Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:ezgoerk] Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:ezgoerk] diff --git a/TypeCobol/Compiler/Diagnostics/TypeCobolLinker.cs b/TypeCobol/Compiler/Diagnostics/TypeCobolLinker.cs index 1da2545d3..52dc60fd8 100644 --- a/TypeCobol/Compiler/Diagnostics/TypeCobolLinker.cs +++ b/TypeCobol/Compiler/Diagnostics/TypeCobolLinker.cs @@ -25,7 +25,7 @@ public override bool BeginCodeElement(CodeElement codeElement) public override bool Visit(DataDescription dataEntry) { TypeReferencer(dataEntry, dataEntry.SymbolTable); - return base.Visit(dataEntry); + return false; //Visit of children is done by TypeReferencer } public override bool Visit(Paragraph paragraph) @@ -45,6 +45,24 @@ public override bool Visit(EnvironmentDivision environmentDivision) public override bool IsStopVisitingChildren => false; + /// + /// Do not visit children of IF. + /// Just in case there are statements directly under the procedure division. + /// + public override bool Visit(If _) => false; + + /// + /// Do not visit children of Evaluate. + /// Just in case there are statements directly under the procedure division. + /// + public override bool Visit(Evaluate _) => false; + + /// + /// Do not visit children of Perform. + /// Just in case there are statements directly under the procedure division. + /// + public override bool Visit(Perform _) => false; + public override bool Visit(Declaratives declaratives) { return false; @@ -80,8 +98,7 @@ private void TypeReferencer(DataDescription dataEntry, SymbolTable symbolTable) //Check the existing children, if they use a type foreach (var child in dataEntry.Children) { - var childDataDesc = child as DataDescription; - if (childDataDesc != null) + if (child is DataDescription childDataDesc) TypeReferencer(childDataDesc, symbolTable); } return; @@ -89,19 +106,24 @@ private void TypeReferencer(DataDescription dataEntry, SymbolTable symbolTable) var type = types.First(); dataEntry.TypeDefinition = type; //Set the TypeDefinition on DataDefinition Node so to avoid symbolTable access - var circularRefInsideChildren = type.Children.Any(c => + //Check circular type reference + if (dataEntry.IsPartOfATypeDef) { - var dataChild = c as DataDescription; - if (dataChild == null) return false; - var childrenType = symbolTable.GetType(dataChild.DataType).FirstOrDefault(); - if (childrenType == null) return false; - return dataEntry.ParentTypeDefinition == childrenType; //Circular reference detected will return true - }); - if (type == dataEntry.ParentTypeDefinition || circularRefInsideChildren) - { - DiagnosticUtils.AddError(dataEntry, "Type circular reference detected", - dataEntry.CodeElement, code: MessageCode.SemanticTCErrorInParser); - return; //Do not continue to prevent further work/crash with circular references + var circularRefInsideChildren = type.Children.Any(c => + { + DataDefinition dataChild = (DataDefinition)c; + var childrenType = symbolTable.GetType(dataChild.DataType).FirstOrDefault(); + if (childrenType == null) return false; + return dataEntry.ParentTypeDefinition == childrenType; //Circular reference detected will return true + }); + + + if (type == dataEntry.ParentTypeDefinition || circularRefInsideChildren) + { + DiagnosticUtils.AddError(dataEntry, "Type circular reference detected", + dataEntry.CodeElement, code: MessageCode.SemanticTCErrorInParser); + return; //Do not continue to prevent further work/crash with circular references + } } if (dataEntry.CodeElement.IsGlobal) From db4f3fb98358c5679d99eab61b9fc27a38433300 Mon Sep 17 00:00:00 2001 From: osmedile Date: Sat, 2 Mar 2019 14:43:51 +0100 Subject: [PATCH 10/11] WI #1114 Fix typo in SymbolTable --- TypeCobol/Compiler/CodeModel/SymbolTable.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TypeCobol/Compiler/CodeModel/SymbolTable.cs b/TypeCobol/Compiler/CodeModel/SymbolTable.cs index 53c57dc91..ada8eb949 100644 --- a/TypeCobol/Compiler/CodeModel/SymbolTable.cs +++ b/TypeCobol/Compiler/CodeModel/SymbolTable.cs @@ -714,17 +714,17 @@ private static IEnumerable GetVariablesUnderTypeDefinition(strin private static IEnumerable GetVariablesUnderTypeDefFromTableAndEnclosing(SymbolTable symbolTable, string name) { var currSymbolTable = symbolTable; - var datadefinitions = new List(); + var dataDefinitions = new List(); //Don't search into Intrinsic table because it's shared between all programs while (currSymbolTable != null && currSymbolTable.CurrentScope != Scope.Intrinsic) { var result = GetVariablesUnderTypeDefinition(name, currSymbolTable); if (result != null) { - datadefinitions.AddRange(result); + dataDefinitions.AddRange(result); } currSymbolTable = currSymbolTable.EnclosingScope; } - return datadefinitions; + return dataDefinitions; } From 73009cbb4bc351188737ab21697c7160f5c0c12c Mon Sep 17 00:00:00 2001 From: osmedile Date: Mon, 4 Mar 2019 21:37:53 +0100 Subject: [PATCH 11/11] WI #1301 SymbolTable : Check only node name to detect filler/unnamed symbol Check node name instead of QualifiedName. This improve performance for all unused variable. For used variables, QualifiedName can be still be used elsewhere in the code. --- TypeCobol/Compiler/CodeModel/SymbolTable.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TypeCobol/Compiler/CodeModel/SymbolTable.cs b/TypeCobol/Compiler/CodeModel/SymbolTable.cs index ada8eb949..d465b0ef8 100644 --- a/TypeCobol/Compiler/CodeModel/SymbolTable.cs +++ b/TypeCobol/Compiler/CodeModel/SymbolTable.cs @@ -1246,12 +1246,13 @@ public IEnumerable GetPrograms() /// private void Add([NotNull] IDictionary> table, [NotNull] T symbol) where T : Node { + string key = symbol.Name; //QualifiedName of symbol can be null - if we have a filler in the type definition - if (symbol.QualifiedName == null) + if (key == null) { return; } - string key = symbol.QualifiedName.Head; + List found; bool present = table.TryGetValue(key, out found); if (!present)