Fix cyclic dependency crash in GetRealUsedUnitsTransitive (#3419)

This commit is contained in:
Александр Земляк 2026-04-05 18:00:41 +03:00 committed by GitHub
parent f61cd7770c
commit 0ffe331599
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 75 additions and 74 deletions

View file

@ -542,47 +542,41 @@ namespace CodeCompletion
/// </summary> /// </summary>
public SymScope[] GetRealUsedUnitsTransitive(bool includeImplementationDependenciesForUnit = true) public SymScope[] GetRealUsedUnitsTransitive(bool includeImplementationDependenciesForUnit = true)
{ {
var usedUnits = new List<SymScope>(); var unitsList = new List<SymScope>();
if (used_units != null) HashSet<SymScope> visited = new HashSet<SymScope>();
{
usedUnits.AddRange(used_units.Where(unit => unit.file_name != null));
}
if (this is InterfaceUnitScope interfaceScope && includeImplementationDependenciesForUnit && interfaceScope.impl_scope != null) void CollectUnitsHelper(SymScope currentScope)
{ {
foreach (var recursiveUsedUnit in interfaceScope.impl_scope.GetRealUsedUnitsTransitive(true)) IEnumerable<SymScope> usedUnitsLocal = currentScope.used_units?.Where(unit => unit.file_name != null);
foreach (var unit in usedUnitsLocal)
{ {
if (recursiveUsedUnit == this) if (unitsList.Find(u => u.file_name == unit.file_name) == null)
continue; unitsList.Add(unit);
}
if (usedUnits.FirstOrDefault(unit => unit.file_name == recursiveUsedUnit.file_name) == null) if (currentScope is InterfaceUnitScope interfaceScope && includeImplementationDependenciesForUnit && interfaceScope.impl_scope != null)
{
CollectUnitsHelper(interfaceScope.impl_scope);
}
foreach (var unit in usedUnitsLocal)
{
if (!visited.Contains(unit))
{ {
usedUnits.Add(recursiveUsedUnit); visited.Add(unit);
CollectUnitsHelper(unit);
} }
} }
} }
if (used_units != null) visited.Add(this);
{ CollectUnitsHelper(this);
foreach (var usedUnit in used_units)
{
var recursiveUsedUnits = usedUnit.GetRealUsedUnitsTransitive(includeImplementationDependenciesForUnit);
foreach (var recursiveUsedUnit in recursiveUsedUnits) unitsList.Remove(this);
{
if (recursiveUsedUnit == this)
continue;
if (usedUnits.FirstOrDefault(unit => unit.file_name == recursiveUsedUnit.file_name) == null) return unitsList.ToArray();
{
usedUnits.Add(recursiveUsedUnit);
}
}
}
}
return usedUnits.ToArray();
} }
public virtual string GetFullName() public virtual string GetFullName()

View file

@ -165,42 +165,46 @@ namespace VisualPascalABC
} }
} }
} }
foreach (string FileName in filesToParse.Keys.ToArray()) // копирование ключей обязательно, иначе будет InvalidOperationException EVA if (recomp_files.Count > 0)
{ {
CodeCompletion.DomConverter dc = CodeCompletion.CodeCompletionController.comp_modules[FileName] as CodeCompletion.DomConverter; foreach (string FileName in filesToParse.Keys.ToArray()) // копирование ключей обязательно, иначе будет InvalidOperationException EVA
if (dc != null)
{ {
CodeCompletion.SymScope watchedUnitScope = dc.visitor.entry_scope; CodeCompletion.DomConverter dc = CodeCompletion.CodeCompletionController.comp_modules[FileName] as CodeCompletion.DomConverter;
if (watchedUnitScope != null)
if (dc != null)
{ {
var usedUnitsTransitive = watchedUnitScope.GetRealUsedUnitsTransitive(); CodeCompletion.SymScope watchedUnitScope = dc.visitor.entry_scope;
if (watchedUnitScope != null)
for (int i = 0; i < usedUnitsTransitive.Length; i++)
{ {
string usedUnitFileName = usedUnitsTransitive[i].file_name; var usedUnitsTransitive = watchedUnitScope.GetRealUsedUnitsTransitive();
// Если какая-то из зависимостей была перекомпилирована for (int i = 0; i < usedUnitsTransitive.Length; i++)
if (recomp_files.Contains(usedUnitFileName))
{ {
// Помечаем нужные модули для будущей перекомпиляции string usedUnitFileName = usedUnitsTransitive[i].file_name;
InvalidateDependentModules(usedUnitsTransitive, usedUnitFileName, filesToParse, FileName);
// Перекомпилируем текущий модуль // Если какая-то из зависимостей была перекомпилирована
is_comp = true; if (recomp_files.Contains(usedUnitFileName))
string text = visualEnvironmentCompiler.SourceFilesProvider(FileName, SourceFileOperation.GetText) as string;
// Здесь третий параметр false, потому что старые данные компиляции еще могут пригодиться до новой перекомпиляции EVA
if (CompileWatchedFile(FileName, text, false))
{ {
// успешная компиляция // Помечаем нужные модули для будущей перекомпиляции
recomp_files.Add(FileName); InvalidateDependentModules(usedUnitsTransitive, usedUnitFileName, filesToParse, FileName);
// Перекомпилируем текущий модуль
is_comp = true;
string text = visualEnvironmentCompiler.SourceFilesProvider(FileName, SourceFileOperation.GetText) as string;
// Здесь третий параметр false, потому что старые данные компиляции еще могут пригодиться до новой перекомпиляции EVA
if (CompileWatchedFile(FileName, text, false))
{
// успешная компиляция
recomp_files.Add(FileName);
}
} }
} }
} }
} }
} }
} }
if (is_comp && mem_delta > 20000000 /*&& mem_delta > 10000000*/) if (is_comp && mem_delta > 20000000 /*&& mem_delta > 10000000*/)
//postavil delta dlja pamjati, posle kototoj delaetsja sborka musora //postavil delta dlja pamjati, posle kototoj delaetsja sborka musora
{ {

View file

@ -170,36 +170,39 @@ namespace VisualPascalABC
} }
} }
} }
foreach (string FileName in filesToParse.Keys.ToArray()) // копирование ключей обязательно, иначе будет InvalidOperationException EVA if (recomp_files.Count > 0)
{ {
CodeCompletion.DomConverter dc = CodeCompletion.CodeCompletionController.comp_modules[FileName] as CodeCompletion.DomConverter; foreach (string FileName in filesToParse.Keys.ToArray()) // копирование ключей обязательно, иначе будет InvalidOperationException EVA
if (dc != null)
{ {
CodeCompletion.SymScope watchedUnitScope = dc.visitor.entry_scope; CodeCompletion.DomConverter dc = CodeCompletion.CodeCompletionController.comp_modules[FileName] as CodeCompletion.DomConverter;
if (watchedUnitScope != null)
if (dc != null)
{ {
var usedUnitsTransitive = watchedUnitScope.GetRealUsedUnitsTransitive(); CodeCompletion.SymScope watchedUnitScope = dc.visitor.entry_scope;
if (watchedUnitScope != null)
for (int i = 0; i < usedUnitsTransitive.Length; i++)
{ {
string usedUnitFileName = usedUnitsTransitive[i].file_name; var usedUnitsTransitive = watchedUnitScope.GetRealUsedUnitsTransitive();
// Если какая-то из зависимостей была перекомпилирована for (int i = 0; i < usedUnitsTransitive.Length; i++)
if (recomp_files.Contains(usedUnitFileName))
{ {
// Помечаем нужные модули для будущей перекомпиляции string usedUnitFileName = usedUnitsTransitive[i].file_name;
InvalidateDependentModules(usedUnitsTransitive, usedUnitFileName, filesToParse, FileName);
// Перекомпилируем текущий модуль // Если какая-то из зависимостей была перекомпилирована
is_comp = true; if (recomp_files.Contains(usedUnitFileName))
string text = visualEnvironmentCompiler.SourceFilesProvider(FileName, SourceFileOperation.GetText) as string;
// Здесь третий параметр false, потому что старые данные компиляции еще могут пригодиться до новой перекомпиляции EVA
if (CompileWatchedFile(FileName, text, false))
{ {
// успешная компиляция // Помечаем нужные модули для будущей перекомпиляции
recomp_files.Add(FileName); InvalidateDependentModules(usedUnitsTransitive, usedUnitFileName, filesToParse, FileName);
// Перекомпилируем текущий модуль
is_comp = true;
string text = visualEnvironmentCompiler.SourceFilesProvider(FileName, SourceFileOperation.GetText) as string;
// Здесь третий параметр false, потому что старые данные компиляции еще могут пригодиться до новой перекомпиляции EVA
if (CompileWatchedFile(FileName, text, false))
{
// успешная компиляция
recomp_files.Add(FileName);
}
} }
} }
} }