Compiler: preload the reference assemblies, add an assembly resolver
This allows the compiler to strictly follow the user-provided reference assembly list. It is very useful for cases when the reference list is huge and not everything in it is in clear state; for example, when a reference assembly list is provided by NuGet transitive closure.
This commit is contained in:
parent
d7d6d24d57
commit
2810132be7
|
|
@ -2637,7 +2637,7 @@ namespace PascalABCCompiler
|
|||
}
|
||||
}
|
||||
|
||||
private string GetReferenceFileName(string FileName, SyntaxTree.SourceContext sc, string curr_path)
|
||||
private string GetReferenceFileName(string FileName, SyntaxTree.SourceContext sc, string curr_path, bool overwrite)
|
||||
{
|
||||
if (standart_assembly_dict.ContainsKey(FileName))
|
||||
return standart_assembly_dict[FileName];
|
||||
|
|
@ -2658,7 +2658,14 @@ namespace PascalABCCompiler
|
|||
if (System.IO.File.Exists(FullFileName))
|
||||
{
|
||||
var NewFileName = Path.Combine(compilerOptions.OutputDirectory, Path.GetFileName(FullFileName));
|
||||
if (FullFileName != NewFileName) File.Copy(FullFileName, NewFileName, true);
|
||||
if (FullFileName != NewFileName)
|
||||
{
|
||||
if (overwrite)
|
||||
File.Copy(FullFileName, NewFileName, true);
|
||||
else if (!File.Exists(NewFileName))
|
||||
File.Copy(FullFileName, NewFileName, false);
|
||||
}
|
||||
|
||||
return NewFileName;
|
||||
}
|
||||
else
|
||||
|
|
@ -2811,16 +2818,20 @@ namespace PascalABCCompiler
|
|||
}
|
||||
}
|
||||
|
||||
private Assembly PreloadReference(TreeRealization.compiler_directive reference)
|
||||
{
|
||||
var sc = GetSourceContext(reference);
|
||||
var fileName = GetReferenceFileName(reference.directive, sc, Path.GetDirectoryName(reference.source_file), true);
|
||||
return NetHelper.NetHelper.PreloadAssembly(fileName);
|
||||
}
|
||||
|
||||
private CompilationUnit CompileReference(PascalABCCompiler.TreeRealization.unit_node_list Units, TreeRealization.compiler_directive cd)
|
||||
{
|
||||
TreeRealization.location loc = cd.location;
|
||||
SyntaxTree.SourceContext sc = null;
|
||||
if (loc != null)
|
||||
sc = new SyntaxTree.SourceContext(loc.begin_line_num, loc.begin_column_num, loc.end_line_num, loc.end_column_num, 0, 0);
|
||||
var sc = GetSourceContext(cd);
|
||||
string UnitName = null;
|
||||
try
|
||||
{
|
||||
UnitName = GetReferenceFileName(cd.directive, sc, Path.GetDirectoryName(cd.source_file));
|
||||
UnitName = GetReferenceFileName(cd.directive, sc, Path.GetDirectoryName(cd.source_file), false);
|
||||
}
|
||||
catch (AssemblyNotFound ex)
|
||||
{
|
||||
|
|
@ -2843,6 +2854,14 @@ namespace PascalABCCompiler
|
|||
//throw new DLLReadingError(UnitName);
|
||||
throw new AssemblyReadingError(CurrentCompilationUnit.SyntaxTree.file_name, UnitName, sc);
|
||||
}
|
||||
|
||||
private SyntaxTree.SourceContext GetSourceContext(TreeRealization.compiler_directive cd)
|
||||
{
|
||||
var loc = cd.location;
|
||||
if (loc == null) return null;
|
||||
return new SyntaxTree.SourceContext(loc.begin_line_num, loc.begin_column_num, loc.end_line_num,
|
||||
loc.end_column_num, 0, 0);
|
||||
}
|
||||
|
||||
private bool HasIncludeNamespacesDirective(CompilationUnit Unit)
|
||||
{
|
||||
|
|
@ -3030,6 +3049,8 @@ namespace PascalABCCompiler
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
var referenceDirectives = new List<TreeRealization.compiler_directive>();
|
||||
foreach (TreeRealization.compiler_directive cd in directives)
|
||||
{
|
||||
if (cd.name.ToLower() == TreeConverter.compiler_string_consts.compiler_directive_reference)
|
||||
|
|
@ -3037,16 +3058,33 @@ namespace PascalABCCompiler
|
|||
if (string.IsNullOrEmpty(cd.directive))
|
||||
throw new TreeConverter.SimpleSemanticError(cd.location, "EXPECTED_ASSEMBLY_NAME");
|
||||
else
|
||||
CompileReference(res, cd);
|
||||
referenceDirectives.Add(cd);
|
||||
}
|
||||
}
|
||||
if (CompilerOptions.ProjectCompiled)
|
||||
{
|
||||
foreach (ReferenceInfo ri in project.references)
|
||||
{
|
||||
CompileReference(res, new TreeRealization.compiler_directive("reference", ri.full_assembly_name, null, project.MainFile));
|
||||
referenceDirectives.Add(new TreeRealization.compiler_directive("reference", ri.full_assembly_name, null, project.MainFile));
|
||||
}
|
||||
}
|
||||
|
||||
// It's important to preload all the referenced assemblies before starting the compilation. During the
|
||||
// compilation, we need to access types from every referenced assembly. An attempt to access them could fail
|
||||
// if a transitively dependent assembly is not loaded, yet.
|
||||
//
|
||||
// It's not always possible to solve by re-ordering the references, since there are cases of
|
||||
// mutually-dependent assemblies (i.e. dependency loops) in the wild.
|
||||
var preloadedAssemblies = new List<Assembly>();
|
||||
foreach (var reference in referenceDirectives)
|
||||
preloadedAssemblies.Add(PreloadReference(reference));
|
||||
|
||||
using (new NetHelper.AssemblyResolveScope(AppDomain.CurrentDomain, preloadedAssemblies))
|
||||
{
|
||||
foreach (var reference in referenceDirectives)
|
||||
CompileReference(res, reference);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
|||
42
TreeConverter/NetWrappers/AssemblyResolveScope.cs
Normal file
42
TreeConverter/NetWrappers/AssemblyResolveScope.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace PascalABCCompiler.NetHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Will force the application domain to load passed assemblies when requested, while ignoring the assembly
|
||||
/// versions. Handles <see cref="AppDomain.AssemblyResolve"/> until disposed.
|
||||
/// </summary>
|
||||
public class AssemblyResolveScope : IDisposable
|
||||
{
|
||||
private readonly AppDomain _appDomain;
|
||||
private readonly Dictionary<string, Assembly> _assemblies = new Dictionary<string, Assembly>();
|
||||
|
||||
public AssemblyResolveScope(AppDomain appDomain, IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
// NOTE: assemblies with identical simple names will overwrite each other
|
||||
_assemblies[assembly.GetName().Name] = assembly;
|
||||
}
|
||||
|
||||
_appDomain = appDomain;
|
||||
_appDomain.AssemblyResolve += OnAssemblyResolve;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_appDomain.AssemblyResolve -= OnAssemblyResolve;
|
||||
}
|
||||
|
||||
private Assembly OnAssemblyResolve(object _, ResolveEventArgs args)
|
||||
{
|
||||
var requestedName = new AssemblyName(args.Name);
|
||||
if (_assemblies.TryGetValue(requestedName.Name, out var assembly))
|
||||
return assembly;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ using System;
|
|||
using PascalABCCompiler.SemanticTree;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
||||
|
|
@ -359,6 +360,12 @@ namespace PascalABCCompiler.NetHelper
|
|||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Assembly PreloadAssembly(string name)
|
||||
{
|
||||
var bytes = File.ReadAllBytes(name);
|
||||
return Assembly.Load(bytes);
|
||||
}
|
||||
|
||||
public static Assembly LoadAssembly(string name)
|
||||
{
|
||||
|
|
@ -408,20 +415,10 @@ namespace PascalABCCompiler.NetHelper
|
|||
}
|
||||
}
|
||||
//if (!System.IO.Path.GetFileName(name).ToLower().Contains("microsoft.directx"))
|
||||
try
|
||||
try
|
||||
{
|
||||
System.IO.FileStream fs = System.IO.File.OpenRead(name);
|
||||
byte[] buf = new byte[fs.Length];
|
||||
fs.Read(buf, 0, (int)fs.Length);
|
||||
fs.Close();
|
||||
curr_inited_assm_path = name;
|
||||
a = System.Reflection.Assembly.Load(buf);
|
||||
|
||||
a.GetTypes();
|
||||
buf = null;
|
||||
//Thread th = new Thread(new ThreadStart(collect_internal));
|
||||
//th.Start();
|
||||
}
|
||||
a = PreloadAssembly(name);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
a = System.Reflection.Assembly.LoadFrom(name);
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@
|
|||
<Compile Include="LambdaExpressions\LambdaProcessingState.cs" />
|
||||
<Compile Include="LambdaExpressions\LambdaResultTypeInferrer.cs" />
|
||||
<Compile Include="LambdaExpressions\LambdaSearcher.cs" />
|
||||
<Compile Include="NetWrappers\AssemblyResolveScope.cs" />
|
||||
<Compile Include="TreeConversion\HasCapturedLambdaParameterInInternalLambdaBody.cs" />
|
||||
<Compile Include="TreeConversion\LambdaHelper.cs" />
|
||||
<Compile Include="SymbolTable\DSST\AreaArrayTable.cs">
|
||||
|
|
|
|||
Loading…
Reference in a new issue