Refactor file searching functions in Compiler (#3111)

* Refactor GetUnitFileName function and used by it

* Revert nested ifs in file searching

* Revert sanity checks

* Return lost checks
This commit is contained in:
AlexanderZemlyak 2024-05-09 11:58:42 +03:00 committed by GitHub
parent 38c4901787
commit 8add90aca0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 116 additions and 120 deletions

View file

@ -380,7 +380,7 @@ namespace CodeCompletion
List<string> Dirs = new List<string>(); List<string> Dirs = new List<string>();
Dirs.AddRange(ddirs); Dirs.AddRange(ddirs);
if (CodeCompletionController.comp != null) if (CodeCompletionController.comp != null)
Dirs.AddRange(CodeCompletionController.comp.CompilerOptions.SearchDirectory); Dirs.AddRange(CodeCompletionController.comp.CompilerOptions.SearchDirectories);
if (CodeCompletionController.StandartDirectories.ContainsKey(LibSourceDirectoryIdent)) if (CodeCompletionController.StandartDirectories.ContainsKey(LibSourceDirectoryIdent))
Dirs.Add((string)CodeCompletionController.StandartDirectories[LibSourceDirectoryIdent]); Dirs.Add((string)CodeCompletionController.StandartDirectories[LibSourceDirectoryIdent]);
return CodeCompletionController.comp.FindSourceFileNameInDirs(unit_name, out found_dir_ind, Dirs.ToArray()); return CodeCompletionController.comp.FindSourceFileNameInDirs(unit_name, out found_dir_ind, Dirs.ToArray());

View file

@ -597,7 +597,7 @@ namespace PascalABCCompiler
public List<string> ForceDefines = new List<string>(); public List<string> ForceDefines = new List<string>();
public List<string> SearchDirectory; public List<string> SearchDirectories;
private bool useDllForSystemUnits = false; private bool useDllForSystemUnits = false;
@ -726,7 +726,7 @@ namespace PascalABCCompiler
StandardDirectories.Add("%PABCSYSTEM%", SystemDirectory); StandardDirectories.Add("%PABCSYSTEM%", SystemDirectory);
ParserSearchPaths = new string[] { Path.Combine(SystemDirectory, "Lib") }; ParserSearchPaths = new string[] { Path.Combine(SystemDirectory, "Lib") };
SearchDirectory = ParserSearchPaths.ToList(); SearchDirectories = ParserSearchPaths.ToList();
} }
@ -1410,7 +1410,7 @@ namespace PascalABCCompiler
}*/ }*/
else else
{ {
string source_file = FindFileInDirs(s + ".vb", out _, Path.GetDirectoryName(CompilerOptions.SourceFileName), Path.Combine(this.CompilerOptions.SystemDirectory, "lib"), string source_file = FindFileWithExtensionInDirs(s + ".vb", out _, Path.GetDirectoryName(CompilerOptions.SourceFileName), Path.Combine(this.CompilerOptions.SystemDirectory, "lib"),
Path.Combine(this.CompilerOptions.SystemDirectory, "LibSource")); Path.Combine(this.CompilerOptions.SystemDirectory, "LibSource"));
if (!string.IsNullOrEmpty(source_file)) if (!string.IsNullOrEmpty(source_file))
{ {
@ -1601,7 +1601,7 @@ namespace PascalABCCompiler
private Assembly IronPythonAssembly; private Assembly IronPythonAssembly;
private MethodInfo PythonCreateEngineMethod; private MethodInfo PythonCreateEngineMethod;
public string CompilePy() /*public string CompilePy()
{ {
OnChangeCompilerState(this, CompilerState.CompilationStarting, CompilerOptions.SourceFileName); OnChangeCompilerState(this, CompilerState.CompilationStarting, CompilerOptions.SourceFileName);
OnChangeCompilerState(this, CompilerState.BeginCompileFile, CompilerOptions.SourceFileName); OnChangeCompilerState(this, CompilerState.BeginCompileFile, CompilerOptions.SourceFileName);
@ -1630,7 +1630,7 @@ namespace PascalABCCompiler
//if (errorsList.Count > 0) //if (errorsList.Count > 0)
return null; return null;
//else return dlls.PathToAssembly; //else return dlls.PathToAssembly;
} }*/
public string CompileCS() public string CompileCS()
{ {
@ -1704,7 +1704,7 @@ namespace PascalABCCompiler
return res.PathToAssembly; return res.PathToAssembly;
} }
public string CompileVB() /*public string CompileVB()
{ {
OnChangeCompilerState(this, CompilerState.CompilationStarting, CompilerOptions.SourceFileName); OnChangeCompilerState(this, CompilerState.CompilationStarting, CompilerOptions.SourceFileName);
OnChangeCompilerState(this, CompilerState.BeginCompileFile, CompilerOptions.SourceFileName); OnChangeCompilerState(this, CompilerState.BeginCompileFile, CompilerOptions.SourceFileName);
@ -1730,8 +1730,8 @@ namespace PascalABCCompiler
parser.Dispose(); parser.Dispose();
if (info != null) if (info != null)
sources.AddRange(info.addit_project_files); sources.AddRange(info.addit_project_files);
string redirect_base_fname = FindFileInDirs("__RedirectIOMode.vb", out _, Path.Combine(this.CompilerOptions.SystemDirectory, "Lib"), Path.Combine(this.CompilerOptions.SystemDirectory, "LibSource")); string redirect_base_fname = FindFileWithExtensionInDirs("__RedirectIOMode.vb", out _, Path.Combine(this.CompilerOptions.SystemDirectory, "Lib"), Path.Combine(this.CompilerOptions.SystemDirectory, "LibSource"));
string system_unit_name = FindFileInDirs("VBSystem.vb", out _, Path.Combine(this.CompilerOptions.SystemDirectory, "lib"), Path.Combine(this.CompilerOptions.SystemDirectory, "LibSource")); string system_unit_name = FindFileWithExtensionInDirs("VBSystem.vb", out _, Path.Combine(this.CompilerOptions.SystemDirectory, "lib"), Path.Combine(this.CompilerOptions.SystemDirectory, "LibSource"));
string redirect_fname = Path.Combine(Path.GetDirectoryName(CompilerOptions.SourceFileName), "_RedirectIOMode.vb"); string redirect_fname = Path.Combine(Path.GetDirectoryName(CompilerOptions.SourceFileName), "_RedirectIOMode.vb");
StreamReader sr = File.OpenText(redirect_base_fname); StreamReader sr = File.OpenText(redirect_base_fname);
string redirect_module = sr.ReadToEnd(); string redirect_module = sr.ReadToEnd();
@ -1823,14 +1823,14 @@ namespace PascalABCCompiler
if (info != null && info.modules.Count > 0) if (info != null && info.modules.Count > 0)
{ {
comp_opt.ReferencedAssemblies.Add(Path.Combine(Path.GetDirectoryName(CompilerOptions.SourceFileName), StringConstants.pabc_rtl_dll_name)); comp_opt.ReferencedAssemblies.Add(Path.Combine(Path.GetDirectoryName(CompilerOptions.SourceFileName), StringConstants.pabc_rtl_dll_name));
string mod_file_name = FindFileInDirs("PABCRtl.dll", out _, Path.Combine(this.CompilerOptions.SystemDirectory, "Lib")); string mod_file_name = FindFileWithExtensionInDirs("PABCRtl.dll", out _, Path.Combine(this.CompilerOptions.SystemDirectory, "Lib"));
File.Copy(mod_file_name, Path.Combine(Path.GetDirectoryName(CompilerOptions.SourceFileName), "PABCRtl.dll"), true); File.Copy(mod_file_name, Path.Combine(Path.GetDirectoryName(CompilerOptions.SourceFileName), "PABCRtl.dll"), true);
/*foreach (string mod in info.modules) *//*foreach (string mod in info.modules)
{ {
comp_opt.ReferencedAssemblies.Add(Path.Combine(Path.GetDirectoryName(CompilerOptions.SourceFileName),mod+".dll")); comp_opt.ReferencedAssemblies.Add(Path.Combine(Path.GetDirectoryName(CompilerOptions.SourceFileName),mod+".dll"));
string mod_file_name = FindSourceFileInDirectories(mod+".mod",Path.Combine(this.CompilerOptions.SystemDirectory,"lib")); string mod_file_name = FindSourceFileInDirectories(mod+".mod",Path.Combine(this.CompilerOptions.SystemDirectory,"lib"));
File.Copy(mod_file_name,Path.Combine(Path.GetDirectoryName(CompilerOptions.SourceFileName),mod+".dll"),true); File.Copy(mod_file_name,Path.Combine(Path.GetDirectoryName(CompilerOptions.SourceFileName),mod+".dll"),true);
}*/ }*//*
} }
sources.Add(redirect_fname); sources.Add(redirect_fname);
sources.Add(system_unit_name); sources.Add(system_unit_name);
@ -1839,7 +1839,7 @@ namespace PascalABCCompiler
{ {
for (int i = 0; i < res.Errors.Count; i++) for (int i = 0; i < res.Errors.Count; i++)
{ {
if (!res.Errors[i].IsWarning && errorsList.Count == 0 /*&& dlls.Errors[i].file_name != redirect_fname*/) if (!res.Errors[i].IsWarning && errorsList.Count == 0 *//*&& dlls.Errors[i].file_name != redirect_fname*//*)
{ {
if (File.Exists(res.Errors[i].FileName)) if (File.Exists(res.Errors[i].FileName))
errorsList.Add(new Errors.CommonCompilerError(res.Errors[i].ErrorText, res.Errors[i].FileName, res.Errors[i].Line != 0 ? res.Errors[i].Line : 1, 1)); errorsList.Add(new Errors.CommonCompilerError(res.Errors[i].ErrorText, res.Errors[i].FileName, res.Errors[i].Line != 0 ? res.Errors[i].Line : 1, 1));
@ -1869,7 +1869,7 @@ namespace PascalABCCompiler
return null; return null;
else else
return res.PathToAssembly; return res.PathToAssembly;
} }*/
private ProjectInfo project; private ProjectInfo project;
@ -2730,68 +2730,73 @@ namespace PascalABCCompiler
return usesSection; return usesSection;
} }
public string FindPCUFileName(string fname, string curr_path, out int folder_priority) public string FindPCUFileName(string fileName, string currentPath, out int folderPriority)
{ {
if (string.IsNullOrEmpty(Path.GetExtension(fname))) if (string.IsNullOrEmpty(Path.GetExtension(fileName)))
fname += CompilerOptions.CompiledUnitExtension; fileName += CompilerOptions.CompiledUnitExtension;
var cache_key = Tuple.Create(fname.ToLower(), curr_path?.ToLower());
if (!PCUFileNamesDictionary.TryGetValue(cache_key, out var res)) var cacheKey = Tuple.Create(fileName.ToLower(), currentPath?.ToLower());
if (!PCUFileNamesDictionary.TryGetValue(cacheKey, out var fileNameWithPriority))
{ {
if (FindFileWithExtensionInDirs(fileName, out _, currentPath) is string resultFileName1)
if (FindFileInDirs(fname, out _, curr_path) is string res_s1) fileNameWithPriority = Tuple.Create(resultFileName1, 1);
res = Tuple.Create(res_s1, 1); else if (FindFileWithExtensionInDirs(Path.GetFileName(fileName), out _, CompilerOptions.OutputDirectory) is string resultFileName2)
else if (FindFileInDirs(Path.GetFileName(fname), out _, CompilerOptions.OutputDirectory) is string res_s2) fileNameWithPriority = Tuple.Create(resultFileName2, 2);
res = Tuple.Create(res_s2, 2); else if (FindFileWithExtensionInDirs(fileName, out var dirIndex, CompilerOptions.SearchDirectories.ToArray()) is string resultFileName3)
else if (FindFileInDirs(fname, out var dir_ind, CompilerOptions.SearchDirectory.ToArray()) is string res_s3) fileNameWithPriority = Tuple.Create(resultFileName3, 3 + dirIndex);
res = Tuple.Create(res_s3, 3 + dir_ind);
else else
res = null; fileNameWithPriority = null;
PCUFileNamesDictionary[cache_key] = res; PCUFileNamesDictionary[cacheKey] = fileNameWithPriority;
} }
folder_priority = res == null ? 0 : res.Item2; folderPriority = fileNameWithPriority?.Item2 ?? 0;
return res?.Item1; return fileNameWithPriority?.Item1;
} }
public string FindSourceFileName(string fname, string curr_path, out int folder_priority) public string FindSourceFileName(string fileName, string currentPath, out int folderPriority)
{ {
var cache_key = Tuple.Create(fname.ToLower(), curr_path?.ToLower()); var cacheKey = Tuple.Create(fileName.ToLower(), currentPath?.ToLower());
if (!SourceFileNamesDictionary.TryGetValue(cache_key, out var res)) if (!SourceFileNamesDictionary.TryGetValue(cacheKey, out var fileNameWithPriority))
{ {
if (FindSourceFileNameInDirs(fileName, out _, currentPath) is string resultFileName1)
if (FindSourceFileNameInDirs(fname, out _, curr_path) is string res_s1) fileNameWithPriority = Tuple.Create(resultFileName1, 1);
res = Tuple.Create(res_s1, 1); else if (FindSourceFileNameInDirs(fileName, out var dirIndex, CompilerOptions.SearchDirectories.ToArray()) is string resultFileName2)
else if (FindSourceFileNameInDirs(fname, out var dir_ind, CompilerOptions.SearchDirectory.ToArray()) is string res_s3) fileNameWithPriority = Tuple.Create(resultFileName2, 3 + dirIndex);
res = Tuple.Create(res_s3, 3 + dir_ind);
else else
res = null; fileNameWithPriority = null;
SourceFileNamesDictionary[cache_key] = res; SourceFileNamesDictionary[cacheKey] = fileNameWithPriority;
} }
folder_priority = res == null ? 0 : res.Item2; folderPriority = fileNameWithPriority?.Item2 ?? 0;
return res?.Item1; return fileNameWithPriority?.Item1;
} }
public string FindSourceFileNameInDirs(string fname, out int found_dir_ind, params string[] Dirs) public string FindSourceFileNameInDirs(string fileName, out int foundDirIndex, params string[] Dirs)
{ {
var fname_ext = Path.GetExtension(fname); var fileNameExtension = Path.GetExtension(fileName);
var need_ext = string.IsNullOrEmpty(fname_ext); var isExtensionEmpty = string.IsNullOrEmpty(fileNameExtension);
// TODO: ищем сперва для расширения текущего языка EVA
foreach (SupportedSourceFile sf in SupportedSourceFiles) foreach (SupportedSourceFile sf in SupportedSourceFiles)
foreach (string ext in sf.Extensions) {
if (need_ext || fname_ext == ext) foreach (string extension in sf.Extensions)
{
if (isExtensionEmpty || fileNameExtension == extension)
{ {
var res = FindFileInDirs(need_ext ? fname + ext : fname, out found_dir_ind, Dirs); var resultFileName = FindFileWithExtensionInDirs(isExtensionEmpty ? fileName + extension : fileName, out foundDirIndex, Dirs);
//if (!(CompilerOptions.UseDllForSystemUnits && Path.GetDirectoryName(dlls) == CompilerOptions.SearchDirectory)) if (resultFileName != null)
if (res != null) return resultFileName;
return res;
} }
}
}
found_dir_ind = 0; foundDirIndex = 0;
return null; return null;
} }
@ -2857,34 +2862,33 @@ namespace PascalABCCompiler
throw new InvalidOperationException($"Could not find path to \"{u2.UnitFileName}\" relative to \"{u1.UnitFileName}\""); throw new InvalidOperationException($"Could not find path to \"{u2.UnitFileName}\" relative to \"{u1.UnitFileName}\"");
} }
private string FindFileInDirs(string FileName, out int found_dir_ind, params string[] Dirs) private string FindFileWithExtensionInDirs(string fileName, out int foundDirIndex, params string[] dirs)
{ {
if (Path.IsPathRooted(FileName)) if (Path.IsPathRooted(fileName))
{ {
found_dir_ind = 0; foundDirIndex = 0;
return File.Exists(FileName) ? FileName : null; return File.Exists(fileName) ? fileName : null;
} }
for (int dir_i = 0; dir_i < Dirs.Length; ++dir_i) for (int dirIndex = 0; dirIndex < dirs.Length; ++dirIndex)
{
try try
{ {
var Dir = Dirs[dir_i]; var dir = dirs[dirIndex];
var res = Path.Combine(Dir, FileName); var resultFileName = Path.Combine(dir, fileName);
if (File.Exists(res)) if (File.Exists(resultFileName))
{ {
found_dir_ind = dir_i; foundDirIndex = dirIndex;
// Path.GetFullPath чтобы нормализовать // Path.GetFullPath чтобы нормализовать
// File.Exists не может кинуть исключение или дать true // File.Exists не может кинуть исключение или дать true
// если путь слишком длинный или содержит нерпавильные знаки // если путь слишком длинный или содержит неправильные знаки
return Path.GetFullPath(res); return Path.GetFullPath(resultFileName);
} }
} }
catch (PathTooLongException) catch (PathTooLongException) { }
{ }
continue;
}
found_dir_ind = 0; foundDirIndex = 0;
return null; return null;
} }
@ -2964,78 +2968,73 @@ namespace PascalABCCompiler
if (currentPath == null) throw new InvalidOperationException(unitNode.UsesPath()); if (currentPath == null) throw new InvalidOperationException(unitNode.UsesPath());
var UnitName = unitNode.name.idents[0].name; var unitName = unitNode.name.idents[0].name;
if (unitNode is SyntaxTree.uses_unit_in uui) if (unitNode is SyntaxTree.uses_unit_in uui)
{ {
TryThrowInvalidPath(uui.in_file.Value, uui.in_file.source_context); TryThrowInvalidPath(uui.in_file.Value, uui.in_file.source_context);
if (UnitName.ToLower() != Path.GetFileNameWithoutExtension(uui.in_file.Value).ToLower()) if (unitName.ToLower() != Path.GetFileNameWithoutExtension(uui.in_file.Value).ToLower())
throw new UsesInWrongName(unitNode.source_context.FileName, UnitName, Path.GetFileNameWithoutExtension(uui.in_file.Value), uui.in_file.source_context); throw new UsesInWrongName(unitNode.source_context.FileName, unitName, Path.GetFileNameWithoutExtension(uui.in_file.Value), uui.in_file.source_context);
} }
return GetUnitFileName(UnitName, unitNode.UsesPath(), currentPath, unitNode.source_context); return GetUnitFileName(unitName, unitNode.UsesPath(), currentPath, unitNode.source_context);
} }
public string GetUnitFileName(string UnitName, string path, string curr_path, SyntaxTree.SourceContext source_context) public string GetUnitFileName(string unitName, string usesPath, string currentPath, SyntaxTree.SourceContext sourceContext)
{ {
var cache_key = Tuple.Create(path.ToLower(), curr_path?.ToLower()); var cacheKey = Tuple.Create(usesPath.ToLower(), currentPath?.ToLower());
string res;
if (GetUnitFileNameCache.TryGetValue(cache_key, out res)) if (GetUnitFileNameCache.TryGetValue(cacheKey, out var unitFileName))
return res; return unitFileName;
// число приоритета меньше = более важная папка // число приоритета меньше означает, что папка более важная
// может выглядеть задом-наперёд, но так должно быть проще в будущем добавлять папки... var sourceFileName = FindSourceFileName(usesPath, currentPath, out var sourceFilePriority);
var SourceFileName = FindSourceFileName(path, curr_path, out var SourceFilePriority); var pcuFileName = FindPCUFileName(usesPath, currentPath, out var pcuFilePriority);
var PCUFileName = FindPCUFileName(path, curr_path, out var PCUFilePriority);
var SourceFileExists = SourceFileName != null; bool sourceFileExists = sourceFileName != null;
//ToDo то есть Rebuild режим игнорирует .pcu, даже если нет .pas файла? bool pcuFileExists = pcuFileName != null;
// а ещё ниже стоит ещё 1 проверка Rebuild...
var PCUFileExists = (!CompilerOptions.Rebuild || !SourceFileExists) && PCUFileName != null;
if (!PCUFileExists && !SourceFileExists) if (CompilerOptions.Rebuild && sourceFileExists)
if (UnitName == null) pcuFileExists = false;
// если нет ни одного типа файла или нет исходника и режим Rebuild, то ошибка
if (!sourceFileExists && !pcuFileExists)
{
if (unitName == null)
// вызов с "unitFileName == null" должен быть только там, где уже известно что хотя бы какой то файл есть // вызов с "unitFileName == null" должен быть только там, где уже известно что хотя бы какой то файл есть
// если где то ещё будет исопльзоваться unitFileName или source_context - надо будет добавить такую же проверку // если где то ещё будет исопльзоваться unitFileName или source_context - надо будет добавить такую же проверку
throw new InvalidOperationException(nameof(UnitName)); throw new InvalidOperationException(nameof(unitName));
else else
throw new UnitNotFound(source_context.FileName, UnitName, source_context); throw new UnitNotFound(sourceContext.FileName, unitName, sourceContext);
}
if (PCUFileExists && SourceFileExists) if (pcuFileExists && sourceFileExists)
{ {
// если приоритет папки исходника выше, то берем исходник
//ToDo из за проверки Rebuild выше - тут всегда будет false if (sourceFilePriority < pcuFilePriority)
// но я не понимаю какая из этих 2 проверок правильная pcuFileExists = false;
if (CompilerOptions.Rebuild && !RecompileList.ContainsKey(PCUFileName)) // проверка на правильность установки приоритета - для удобства
PCUFileExists = false; else if (sourceFilePriority == pcuFilePriority && Path.GetDirectoryName(sourceFileName) != Path.GetDirectoryName(pcuFileName))
throw new InvalidOperationException("Не верно задан приоритет папок!");
else if (SourceFilePriority != PCUFilePriority) // также если время модификации pcu раньше, чем исходника, то нужна перекомпиляция (при этом они должны быть в одной папке)
{ else if (sourceFilePriority == pcuFilePriority && File.GetLastWriteTime(pcuFileName) < File.GetLastWriteTime(sourceFileName))
if (SourceFilePriority < PCUFilePriority) pcuFileExists = false;
PCUFileExists = false;
else
SourceFileExists = false;
}
else if (Path.GetDirectoryName(SourceFileName) != Path.GetDirectoryName(PCUFileName))
throw new InvalidOperationException("priority"); // не должно происходить, раз приоритет одинаковый
else if (File.GetLastWriteTime(PCUFileName) < File.GetLastWriteTime(SourceFileName))
PCUFileExists = false;
} }
if (PCUFileExists) if (pcuFileExists)
res = Path.Combine(curr_path, PCUFileName); unitFileName = Path.Combine(currentPath, pcuFileName);
else if (SourceFileExists) else if (sourceFileExists)
res = Path.Combine(curr_path, SourceFileName); unitFileName = Path.Combine(currentPath, sourceFileName);
else else
throw new InvalidOperationException(nameof(SourceFileExists)); // тело "if (PCUFileExists && SourceFileExists)" не должно присваивать false обоим переменным // значит в предыдущем блоке кода ошибка - проверка для удобства
throw new InvalidOperationException("Сброшено значение pcuFileExists и sourceFileExists. Такого здесь быть не должно.");
GetUnitFileNameCache[cache_key] = res; GetUnitFileNameCache[cacheKey] = unitFileName;
return res; return unitFileName;
} }
public void AddStandardUnitsToInterfaceUsesSection(SyntaxTree.compilation_unit unitSyntaxTree) public void AddStandardUnitsToInterfaceUsesSection(SyntaxTree.compilation_unit unitSyntaxTree)
@ -3541,9 +3540,6 @@ namespace PascalABCCompiler
return null; return null;
} }
/// <summary>
/// получение списка using - legacy code !!!
/// </summary>
public string GetSourceFileText(string FileName) public string GetSourceFileText(string FileName)
{ {
return (string)SourceFilesProvider(FileName, SourceFileOperation.GetText); return (string)SourceFilesProvider(FileName, SourceFileOperation.GetText);

View file

@ -95,7 +95,7 @@ namespace PascalABCCompiler
Console.WriteLine($"SearchDir \"{value}\" not found relative to \"{Environment.CurrentDirectory}\""); Console.WriteLine($"SearchDir \"{value}\" not found relative to \"{Environment.CurrentDirectory}\"");
return false; return false;
} }
co.SearchDirectory.Insert(0, value); // .Insert, чтобы определённые пользователем папки имели бОльший приоритет, чем стандартная co.SearchDirectories.Insert(0, value); // .Insert, чтобы определённые пользователем папки имели бОльший приоритет, чем стандартная
return true; return true;
case "locale": case "locale":