mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 12:30:32 +00:00
Compiler fixes. Updated project.json files. Removed *.lock.json files and updated gitignore to ignore them. Updated some Cosmos.Debug projects.
492 lines
19 KiB
C#
492 lines
19 KiB
C#
#define COSMOSDEBUG
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
|
|
using Cosmos.Build.Common;
|
|
using Cosmos.Debug.Symbols;
|
|
|
|
namespace Cosmos.IL2CPU
|
|
{
|
|
// http://blogs.msdn.com/b/visualstudio/archive/2010/07/06/debugging-msbuild-script-with-visual-studio.aspx
|
|
public class CompilerEngine
|
|
{
|
|
const string FULLASSEMBLYNAME_KERNEL = "Cosmos.System.Kernel";
|
|
|
|
public Action<string> OnLogMessage;
|
|
public Action<string> OnLogError;
|
|
public Action<string> OnLogWarning;
|
|
public Action<Exception> OnLogException;
|
|
protected static Action<string> mStaticLog = null;
|
|
|
|
public string DebugMode { get; set; }
|
|
public string TraceAssemblies { get; set; }
|
|
public byte DebugCom { get; set; }
|
|
public bool UseNAsm { get; set; }
|
|
public string[] References { get; set; }
|
|
public string OutputFilename { get; set; }
|
|
public bool EnableLogging { get; set; }
|
|
public bool EmitDebugSymbols { get; set; }
|
|
public bool IgnoreDebugStubAttribute { get; set; }
|
|
public string StackCorruptionDetectionLevel { get; set; }
|
|
public string[] AdditionalSearchDirs { get; set; }
|
|
public string[] AdditionalReferences { get; set; }
|
|
|
|
private List<CompilerExtensionBase> mLoadedExtensions;
|
|
|
|
public bool DebugEnabled = false;
|
|
public bool StackCorruptionDetectionEnabled = false;
|
|
protected StackCorruptionDetectionLevel mStackCorruptionDetectionLevel = Cosmos.Build.Common.StackCorruptionDetectionLevel.MethodFooters;
|
|
protected DebugMode mDebugMode = Cosmos.Build.Common.DebugMode.Source;
|
|
protected TraceAssemblies mTraceAssemblies = Cosmos.Build.Common.TraceAssemblies.All;
|
|
protected static List<string> mSearchDirs = new List<string>();
|
|
|
|
public string AssemblerLog = "Cosmos.Assembler.log";
|
|
|
|
protected void LogTime(string message)
|
|
{
|
|
}
|
|
|
|
protected void LogMessage(string aMsg)
|
|
{
|
|
OnLogMessage?.Invoke(aMsg);
|
|
}
|
|
|
|
protected void LogWarning(string aMsg)
|
|
{
|
|
OnLogWarning?.Invoke(aMsg);
|
|
}
|
|
|
|
protected void LogError(string aMsg)
|
|
{
|
|
OnLogError?.Invoke(aMsg);
|
|
}
|
|
|
|
protected void LogException(Exception e)
|
|
{
|
|
OnLogException?.Invoke(e);
|
|
}
|
|
|
|
private string CurrentDomain_AssemblyPath(string aShortName, Assembly aRequestingAssembly)
|
|
{
|
|
// Check nuget packages.
|
|
foreach (var xRef in AdditionalReferences)
|
|
{
|
|
var xAssemblyName = AssemblyName.GetAssemblyName(xRef);
|
|
if (xAssemblyName.Name == aShortName)
|
|
{
|
|
return xRef;
|
|
}
|
|
}
|
|
|
|
// Check search directories.
|
|
foreach (var xDir in mSearchDirs)
|
|
{
|
|
var xPath = Path.Combine(xDir, aShortName + ".dll");
|
|
if (File.Exists(xPath))
|
|
{
|
|
return xPath;
|
|
}
|
|
xPath = Path.Combine(xDir, aShortName + ".exe");
|
|
if (File.Exists(xPath))
|
|
{
|
|
return xPath;
|
|
}
|
|
}
|
|
|
|
if (aRequestingAssembly != null)
|
|
{
|
|
|
|
// check for path in as requested dll is stored, this makes refrenced dll project working
|
|
var xPathAsRequested = Path.Combine(Path.GetDirectoryName(aRequestingAssembly.Location), aShortName + ".dll");
|
|
if (File.Exists(xPathAsRequested))
|
|
{
|
|
return xPathAsRequested;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
|
{
|
|
CompilerHelpers.Debug($"Resolving assembly '{args.Name}'.");
|
|
|
|
var xShortName = args.Name;
|
|
if (xShortName.Contains(','))
|
|
{
|
|
xShortName = xShortName.Substring(0, xShortName.IndexOf(','));
|
|
}
|
|
|
|
// Check already loaded assemblies.
|
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
|
{
|
|
var xLoadedShortName = assembly.GetName().Name;
|
|
if (xLoadedShortName == xShortName)
|
|
{
|
|
return assembly;
|
|
}
|
|
}
|
|
|
|
string xPath = CurrentDomain_AssemblyPath(xShortName, args.RequestingAssembly);
|
|
if (!string.IsNullOrWhiteSpace(xPath))
|
|
{
|
|
return Assembly.LoadFrom(xPath);
|
|
}
|
|
|
|
mStaticLog?.Invoke($"Assembly '{args.Name}' not resolved!");
|
|
return null;
|
|
}
|
|
|
|
private Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
|
|
{
|
|
CompilerHelpers.Debug($"Resolving assembly '{args.Name}'.");
|
|
|
|
var xShortName = args.Name;
|
|
if (xShortName.Contains(','))
|
|
{
|
|
xShortName = xShortName.Substring(0, xShortName.IndexOf(','));
|
|
}
|
|
|
|
// Check already loaded assemblies.
|
|
foreach (var assembly in AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies())
|
|
{
|
|
var xLoadedShortName = assembly.GetName().Name;
|
|
if (xLoadedShortName == xShortName)
|
|
{
|
|
return assembly;
|
|
}
|
|
}
|
|
|
|
string xPath = CurrentDomain_AssemblyPath(xShortName, args.RequestingAssembly);
|
|
if (!string.IsNullOrWhiteSpace(xPath))
|
|
{
|
|
return Assembly.ReflectionOnlyLoadFrom(xPath);
|
|
}
|
|
|
|
mStaticLog?.Invoke($"Assembly '{args.Name}' not resolved!");
|
|
return null;
|
|
}
|
|
|
|
private bool EnsureCosmosPathsInitialization()
|
|
{
|
|
try
|
|
{
|
|
CosmosPaths.Initialize();
|
|
return true;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
StringBuilder builder = new StringBuilder();
|
|
builder.Append("Error while initializing Cosmos paths");
|
|
for (Exception scannedException = e; null != scannedException; scannedException = scannedException.InnerException)
|
|
{
|
|
builder.Append(" | " + scannedException.Message);
|
|
}
|
|
LogError(builder.ToString());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected bool Initialize()
|
|
{
|
|
if (!EnsureCosmosPathsInitialization())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (AdditionalSearchDirs != null)
|
|
{
|
|
mSearchDirs.AddRange(AdditionalSearchDirs);
|
|
}
|
|
|
|
// Add UserKit dirs for asms to load from.
|
|
mSearchDirs.Add(Path.GetDirectoryName(typeof(CompilerEngine).GetTypeInfo().Assembly.Location));
|
|
mSearchDirs.Add(CosmosPaths.UserKit);
|
|
mSearchDirs.Add(CosmosPaths.Kernel);
|
|
|
|
PlugManager.AdditionalReferences = AdditionalReferences;
|
|
|
|
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
|
|
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_ReflectionOnlyAssemblyResolve;
|
|
|
|
mDebugMode = (DebugMode)Enum.Parse(typeof(DebugMode), DebugMode);
|
|
if (string.IsNullOrEmpty(TraceAssemblies))
|
|
{
|
|
mTraceAssemblies = Cosmos.Build.Common.TraceAssemblies.User;
|
|
}
|
|
else
|
|
{
|
|
if (!Enum.GetNames(typeof(TraceAssemblies)).Contains(TraceAssemblies, StringComparer.OrdinalIgnoreCase))
|
|
{
|
|
LogError("Invalid TraceAssemblies specified");
|
|
return false;
|
|
}
|
|
mTraceAssemblies = (TraceAssemblies)Enum.Parse(typeof(TraceAssemblies), TraceAssemblies);
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(StackCorruptionDetectionLevel))
|
|
{
|
|
mStackCorruptionDetectionLevel = Cosmos.Build.Common.StackCorruptionDetectionLevel.MethodFooters;
|
|
}
|
|
else
|
|
{
|
|
mStackCorruptionDetectionLevel = (StackCorruptionDetectionLevel)Enum.Parse(typeof(StackCorruptionDetectionLevel), StackCorruptionDetectionLevel);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool Execute()
|
|
{
|
|
try
|
|
{
|
|
LogMessage("Executing IL2CPU on assembly");
|
|
if (!Initialize())
|
|
{
|
|
return false;
|
|
}
|
|
LogTime("Engine execute started");
|
|
|
|
// Find the kernel's entry point. We are looking for a public class Kernel, with public static void Boot()
|
|
var xInitMethod = LoadAssemblies();
|
|
if (xInitMethod == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var xOutputFilename = Path.Combine(Path.GetDirectoryName(OutputFilename), Path.GetFileNameWithoutExtension(OutputFilename));
|
|
if (!DebugEnabled)
|
|
{
|
|
// Default of 1 is in Cosmos.Targets. Need to change to use proj props.
|
|
DebugCom = 0;
|
|
}
|
|
|
|
using (var xAsm = GetAppAssembler())
|
|
{
|
|
using (var xDebugInfo = new DebugInfo(xOutputFilename + ".cdb", true, false))
|
|
{
|
|
xAsm.DebugInfo = xDebugInfo;
|
|
xAsm.DebugEnabled = DebugEnabled;
|
|
xAsm.StackCorruptionDetection = StackCorruptionDetectionEnabled;
|
|
xAsm.StackCorruptionDetectionLevel = mStackCorruptionDetectionLevel;
|
|
xAsm.DebugMode = mDebugMode;
|
|
xAsm.TraceAssemblies = mTraceAssemblies;
|
|
xAsm.IgnoreDebugStubAttribute = IgnoreDebugStubAttribute;
|
|
if (DebugEnabled == false)
|
|
{
|
|
xAsm.ShouldOptimize = true;
|
|
}
|
|
|
|
xAsm.Assembler.Initialize();
|
|
using (var xScanner = new ILScanner(xAsm))
|
|
{
|
|
xScanner.LogException = LogException;
|
|
xScanner.LogWarning = LogWarning;
|
|
CompilerHelpers.DebugEvent += LogMessage;
|
|
if (EnableLogging)
|
|
{
|
|
var xLogFile = xOutputFilename + ".log.html";
|
|
if (false == xScanner.EnableLogging(xLogFile))
|
|
{
|
|
// file creation not possible
|
|
EnableLogging = false;
|
|
LogWarning("Could not create the file \"" + xLogFile + "\"! No log will be created!");
|
|
}
|
|
}
|
|
xScanner.QueueMethod(xInitMethod.DeclaringType.GetTypeInfo().BaseType.GetTypeInfo().GetMethod("Start"));
|
|
xScanner.Execute(xInitMethod);
|
|
|
|
AppAssemblerRingsCheck.Execute(xScanner, xInitMethod.DeclaringType.GetTypeInfo().Assembly);
|
|
|
|
using (var xOut = new StreamWriter(File.OpenWrite(OutputFilename), Encoding.ASCII, 128 * 1024))
|
|
{
|
|
//if (EmitDebugSymbols) {
|
|
xAsm.Assembler.FlushText(xOut);
|
|
xAsm.FinalizeDebugInfo();
|
|
//// for now: write debug info to console
|
|
//Console.WriteLine("Wrote {0} instructions and {1} datamembers", xAsm.Assembler.Instructions.Count, xAsm.Assembler.DataMembers.Count);
|
|
//var dict = new Dictionary<string, long>(StringComparer.OrdinalIgnoreCase);
|
|
//foreach (var instr in xAsm.Assembler.Instructions)
|
|
//{
|
|
// var mn = instr.Mnemonic ?? "";
|
|
// if (dict.ContainsKey(mn))
|
|
// {
|
|
// dict[mn] = dict[mn] + 1;
|
|
// }
|
|
// else
|
|
// {
|
|
// dict[mn] = 1;
|
|
// }
|
|
//}
|
|
//foreach (var entry in dict)
|
|
//{
|
|
// Console.WriteLine("{0}|{1}", entry.Key, entry.Value);
|
|
//}
|
|
}
|
|
}
|
|
// If you want to uncomment this line make sure to enable PERSISTANCE_PROFILING symbol in
|
|
// DebugInfo.cs file.
|
|
//LogMessage(string.Format("DebugInfo flatening {0} seconds, persistance : {1} seconds",
|
|
// (int)xDebugInfo.FlateningDuration.TotalSeconds,
|
|
// (int)xDebugInfo.PersistanceDuration.TotalSeconds));
|
|
}
|
|
}
|
|
LogTime("Engine execute finished");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogException(ex);
|
|
LogMessage("Loaded assemblies: ");
|
|
foreach (var xAsm in AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies())
|
|
{
|
|
// HACK: find another way to skip dynamic assemblies (which belong to dynamic methods)
|
|
if (xAsm.IsDynamic)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
try
|
|
{
|
|
LogMessage(xAsm.Location);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private AppAssembler GetAppAssembler()
|
|
{
|
|
if (mLoadedExtensions == null)
|
|
{
|
|
throw new InvalidOperationException("Extensions have not been loaded!");
|
|
}
|
|
foreach (var xExt in mLoadedExtensions)
|
|
{
|
|
AppAssembler xResult;
|
|
if (xExt.TryCreateAppAssembler(DebugCom, AssemblerLog, out xResult))
|
|
{
|
|
return xResult;
|
|
}
|
|
}
|
|
|
|
return new AppAssembler(DebugCom, AssemblerLog);
|
|
}
|
|
|
|
private void LoadReferences(Assembly xAssembly)
|
|
{
|
|
foreach (var a in xAssembly.GetReferencedAssemblies())
|
|
{
|
|
string s = null;
|
|
foreach (var xAsm in AdditionalReferences)
|
|
{
|
|
try
|
|
{
|
|
|
|
s = AssemblyName.GetAssemblyName(xAsm).Name;
|
|
if (s == null || s != a.Name)
|
|
{
|
|
s = null;
|
|
}
|
|
}
|
|
catch (BadImageFormatException e)
|
|
{
|
|
}
|
|
}
|
|
if (AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(m => m.GetName().Name == a.Name) == null)
|
|
{
|
|
if (s != null)
|
|
{
|
|
var xAsm = Assembly.ReflectionOnlyLoadFrom(s);
|
|
x.Add(xAsm);
|
|
if (xAsm.GetReferencedAssemblies().Any())
|
|
{
|
|
LoadReferences(xAsm);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
List<Assembly> x = new List<Assembly>();
|
|
|
|
/// <summary>Load every refernced assemblies that have an associated FullPath property and seek for
|
|
/// the kernel default constructor.</summary>
|
|
/// <returns>The kernel default constructor or a null reference if either none or several such
|
|
/// constructor could be found.</returns>
|
|
private MethodBase LoadAssemblies()
|
|
{
|
|
// Try to load explicit path references.
|
|
// These are the references of our boot project. We dont actually ever load the boot
|
|
// project asm. Instead the references will contain plugs, and the kernel. We load
|
|
// them then find the entry point in the kernel.
|
|
//
|
|
// Plugs and refs in this list will be loaded absolute (or as proj refs) only. Asm resolution
|
|
// will not be tried on them, but will on ASMs they reference.
|
|
//
|
|
|
|
mLoadedExtensions = new List<CompilerExtensionBase>();
|
|
Type xKernelType = null;
|
|
|
|
foreach (string xReference in References)
|
|
{
|
|
if (File.Exists(xReference))
|
|
{
|
|
var xAssembly = Assembly.ReflectionOnlyLoadFrom(xReference);
|
|
|
|
CompilerHelpers.Debug($"Looking for kernel in '{xAssembly}'");
|
|
|
|
foreach (var xType in xAssembly.ExportedTypes)
|
|
{
|
|
if (!xType.IsGenericTypeDefinition && !xType.IsAbstract)
|
|
{
|
|
CompilerHelpers.Debug($"Checking type '{xType.FullName}'");
|
|
|
|
if (xType.GetTypeInfo().IsSubclassOf(typeof(Cosmos.System.Kernel)))
|
|
{
|
|
// found kernel?
|
|
if (xKernelType != null)
|
|
{
|
|
// already a kernel found, which is not supported.
|
|
LogError($"Two kernels found! '{xType.AssemblyQualifiedName}' and '{xKernelType.AssemblyQualifiedName}'");
|
|
return null;
|
|
}
|
|
xKernelType = xType;
|
|
}
|
|
}
|
|
}
|
|
|
|
var xCompilerExtensionsMetas = xAssembly.GetCustomAttributes<CompilerExtensionAttribute>();
|
|
foreach (var xMeta in xCompilerExtensionsMetas)
|
|
{
|
|
mLoadedExtensions.Add((CompilerExtensionBase) Activator.CreateInstance(xMeta.Type));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (xKernelType == null)
|
|
{
|
|
LogError("No Kernel found!");
|
|
return null;
|
|
}
|
|
var xCtor = xKernelType.GetTypeInfo().GetConstructor(Type.EmptyTypes);
|
|
if (xCtor == null)
|
|
{
|
|
LogError("Kernel has no public default constructor");
|
|
return null;
|
|
}
|
|
return xCtor;
|
|
}
|
|
}
|
|
}
|