Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23023.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILToNative.DependencyAnalysisFramework.Tests", "tests\ILToNative.DependencyAnalysisFramework.Tests.csproj", "{90076B9B-918B-49DD-8ADE-E76426D60B4D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILToNative.DependencyAnalysisFramework", "src\ILToNative.DependencyAnalysisFramework.csproj", "{DAC23E9F-F826-4577-AE7A-0849FF83280C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{90076B9B-918B-49DD-8ADE-E76426D60B4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{90076B9B-918B-49DD-8ADE-E76426D60B4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90076B9B-918B-49DD-8ADE-E76426D60B4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90076B9B-918B-49DD-8ADE-E76426D60B4D}.Release|Any CPU.Build.0 = Release|Any CPU
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

namespace ILToNative.DependencyAnalysisFramework
{
public abstract class ComputedStaticDependencyNode<DependencyContextType> : DependencyNodeCore<DependencyContextType>
{
private IEnumerable<DependencyListEntry> _dependencies;
private IEnumerable<CombinedDependencyListEntry> _conditionalDependencies;
private static CombinedDependencyListEntry[] s_emptyDynamicList = new CombinedDependencyListEntry[0];

public void SetStaticDependencies(IEnumerable<DependencyListEntry> dependencies,
IEnumerable<CombinedDependencyListEntry> conditionalDependencies)
{
Debug.Assert(_dependencies == null);
Debug.Assert(_conditionalDependencies == null);
Debug.Assert(dependencies != null);

_dependencies = dependencies;
_conditionalDependencies = conditionalDependencies;
}

public override bool HasConditionalStaticDependencies
{
get
{
return _conditionalDependencies != null;
}
}

public override bool HasDynamicDependencies
{
get
{
return false;
}
}

public override bool InterestingForDynamicDependencyAnalysis
{
get
{
return true;
}
}

public override bool StaticDependenciesAreComputed
{
get
{
return _dependencies != null;
}
}

public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(DependencyContextType context)
{
return _conditionalDependencies;
}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(DependencyContextType context)
{
return _dependencies;
}

public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<DependencyContextType>> markedNodes, int firstNode, DependencyContextType context)
{
return s_emptyDynamicList;
}
}
}
280 changes: 280 additions & 0 deletions src/ILToNative.DependencyAnalysisFramework/src/DependencyAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;

namespace ILToNative.DependencyAnalysisFramework
{
/// <summary>
/// Implement a dependency analysis framework. This works much like a Garbage Collector's mark algorithm
/// in that it finds a set of nodes from an initial root set.
///
/// However, in contrast to a typical GC in addition to simple edges from a node, there may also
/// be conditional edges where a node has a dependency if some other specific node exists in the
/// graph, and dynamic edges in which a node has a dependency if some other node exists in the graph,
/// but what that other node might be is not known until it may exist in the graph.
///
/// This analyzer also attempts to maintain a serialized state of why nodes are in the graph
/// with strings describing the reason a given node was added to the graph. The degree of logging
/// is configurable via the MarkStrategy
///
/// </summary>
public sealed class DependencyAnalyzer<MarkStrategy, DependencyContextType> : DependencyAnalyzerBase<DependencyContextType> where MarkStrategy: struct, IDependencyAnalysisMarkStrategy<DependencyContextType>
{
private MarkStrategy _marker = new MarkStrategy();
private DependencyContextType _dependencyContext;
private IComparer<DependencyNodeCore<DependencyContextType>> _resultSorter = null;

private Stack<DependencyNodeCore<DependencyContextType>> _markStack = new Stack<DependencyNodeCore<DependencyContextType>>();
private List<DependencyNodeCore<DependencyContextType>> _markedNodes = new List<DependencyNodeCore<DependencyContextType>>();
private ImmutableArray<DependencyNodeCore<DependencyContextType>> _markedNodesFinal;
private List<DependencyNodeCore<DependencyContextType>> _rootNodes = new List<DependencyNodeCore<DependencyContextType>>();
private List<DependencyNodeCore<DependencyContextType>> _deferredStaticDependencies = new List<DependencyNodeCore<DependencyContextType>>();
private List<DependencyNodeCore<DependencyContextType>> _dynamicDependencyInterestingList = new List<DependencyNodeCore<DependencyContextType>>();
private List<DynamicDependencyNode> _markedNodesWithDynamicDependencies = new List<DynamicDependencyNode>();
private bool _newDynamicDependenciesMayHaveAppeared = false;

private Dictionary<DependencyNodeCore<DependencyContextType>, HashSet<DependencyNodeCore<DependencyContextType>.CombinedDependencyListEntry>> _conditional_dependency_store = new Dictionary<DependencyNodeCore<DependencyContextType>, HashSet<DependencyNodeCore<DependencyContextType>.CombinedDependencyListEntry>>();
private bool _markingCompleted = false;

private struct DynamicDependencyNode
{
private DependencyNodeCore<DependencyContextType> _node;
private int _next;

public DynamicDependencyNode(DependencyNodeCore<DependencyContextType> node)
{
_node = node;
_next = 0;
}

public void MarkNewDynamicDependencies(DependencyAnalyzer<MarkStrategy, DependencyContextType> analyzer)
{
foreach (DependencyNodeCore<DependencyContextType>.CombinedDependencyListEntry dependency in
_node.SearchDynamicDependencies(analyzer._dynamicDependencyInterestingList, _next, analyzer._dependencyContext))
{
analyzer.AddToMarkStack(dependency.Node, dependency.Reason, _node, dependency.OtherReasonNode);
}
_next = analyzer._dynamicDependencyInterestingList.Count;
}
}

// Api surface
public DependencyAnalyzer(DependencyContextType dependencyContext, IComparer<DependencyNodeCore<DependencyContextType>> resultSorter)
{
_dependencyContext = dependencyContext;
_resultSorter = resultSorter;
}

/// <summary>
/// Add a root node
/// </summary>
public override sealed void AddRoot(DependencyNodeCore<DependencyContextType> rootNode, string reason)
{
if (AddToMarkStack(rootNode, reason, null, null))
{
_rootNodes.Add(rootNode);
}
}

public override sealed ImmutableArray<DependencyNodeCore<DependencyContextType>> MarkedNodeList
{
get
{
if (!_markingCompleted)
{
_markingCompleted = true;
ComputeMarkedNodes();
}

return _markedNodesFinal;
}
}

public override sealed event Action<DependencyNodeCore<DependencyContextType>> NewMarkedNode;

public override sealed event Action<List<DependencyNodeCore<DependencyContextType>>> ComputeDependencyRoutine;


private IEnumerable<DependencyNodeCore<DependencyContextType>> MarkedNodesEnumerable()
{
if (_markedNodesFinal != null)
return _markedNodesFinal;
else
return _markedNodes;
}

public override sealed void VisitLogNodes(IDependencyAnalyzerLogNodeVisitor logNodeVisitor)
{
foreach (DependencyNode node in MarkedNodesEnumerable())
{
logNodeVisitor.VisitNode(node);
}
_marker.VisitLogNodes(MarkedNodesEnumerable(), logNodeVisitor);
}

public override sealed void VisitLogEdges(IDependencyAnalyzerLogEdgeVisitor logEdgeVisitor)
{
_marker.VisitLogEdges(MarkedNodesEnumerable(), logEdgeVisitor);
}


/// <summary>
/// Called by the algorithm to ensure that this set of nodes is processed such that static dependencies are computed.
/// </summary>
/// <param name="deferredStaticDependencies">List of nodes which must have static dependencies computed</param>
private void ComputeDependencies(List<DependencyNodeCore<DependencyContextType>> deferredStaticDependencies)
{
if (ComputeDependencyRoutine != null)
ComputeDependencyRoutine(deferredStaticDependencies);
}

// Internal details
void GetStaticDependenciesImpl(DependencyNodeCore<DependencyContextType> node)
{
foreach (DependencyNodeCore<DependencyContextType>.DependencyListEntry dependency in node.GetStaticDependencies(_dependencyContext))
{
AddToMarkStack(dependency.Node, dependency.Reason, node, null);
}

if (node.HasConditionalStaticDependencies)
{
foreach (DependencyNodeCore<DependencyContextType>.CombinedDependencyListEntry dependency in node.GetConditionalStaticDependencies(_dependencyContext))
{
if (dependency.OtherReasonNode.Marked)
{
AddToMarkStack(dependency.Node, dependency.Reason, node, dependency.OtherReasonNode);
}
else
{
HashSet<DependencyNodeCore<DependencyContextType>.CombinedDependencyListEntry> storedDependencySet = null;
if (!_conditional_dependency_store.TryGetValue(dependency.OtherReasonNode, out storedDependencySet))
{
storedDependencySet = new HashSet<DependencyNodeCore<DependencyContextType>.CombinedDependencyListEntry>();
_conditional_dependency_store.Add(dependency.OtherReasonNode, storedDependencySet);
}
// Swap out other reason node as we're storing that as the dictionary key
DependencyNodeCore<DependencyContextType>.CombinedDependencyListEntry conditionalDependencyStoreEntry = new DependencyNodeCore<DependencyContextType>.CombinedDependencyListEntry();
conditionalDependencyStoreEntry.Node = dependency.Node;
conditionalDependencyStoreEntry.Reason = dependency.Reason;
conditionalDependencyStoreEntry.OtherReasonNode = node;

storedDependencySet.Add(conditionalDependencyStoreEntry);
}
}
}
}

private void GetStaticDependencies(DependencyNodeCore<DependencyContextType> node)
{
if (node.StaticDependenciesAreComputed)
{
GetStaticDependenciesImpl(node);
}
else
{
_deferredStaticDependencies.Add(node);
}
}

private void ProcessMarkStack()
{
do
{
while (_markStack.Count > 0)
{
// Pop the top node of the mark stack
DependencyNodeCore<DependencyContextType> currentNode = _markStack.Pop();

Debug.Assert(currentNode.Marked);

// Only some marked objects are interesting for dynamic dependencies
// store those in a seperate list to avoid excess scanning over non-interesting
// nodes during dynamic dependency discovery
if (currentNode.InterestingForDynamicDependencyAnalysis)
{
_dynamicDependencyInterestingList.Add(currentNode);
_newDynamicDependenciesMayHaveAppeared = true;
}

// Add all static dependencies to the mark stack
GetStaticDependencies(currentNode);

// If there are dynamic dependencies, note for later
if (currentNode.HasDynamicDependencies)
{
_newDynamicDependenciesMayHaveAppeared = true;
_markedNodesWithDynamicDependencies.Add(new DynamicDependencyNode(currentNode));
}

// If this new node satisfies any stored conditional dependencies,
// add them to the mark stack
HashSet<DependencyNodeCore<DependencyContextType>.CombinedDependencyListEntry> storedDependencySet = null;
if (_conditional_dependency_store.TryGetValue(currentNode, out storedDependencySet))
{
foreach (DependencyNodeCore<DependencyContextType>.CombinedDependencyListEntry newlySatisfiedDependency in storedDependencySet)
{
AddToMarkStack(newlySatisfiedDependency.Node, newlySatisfiedDependency.Reason, newlySatisfiedDependency.OtherReasonNode, currentNode);
}

_conditional_dependency_store.Remove(currentNode);
}

if (NewMarkedNode != null)
NewMarkedNode(currentNode);
}

// Find new dependencies introduced by dynamic depedencies
if (_newDynamicDependenciesMayHaveAppeared)
{
_newDynamicDependenciesMayHaveAppeared = false;
foreach (DynamicDependencyNode dynamicNode in _markedNodesWithDynamicDependencies)
{
dynamicNode.MarkNewDynamicDependencies(this);
}
}
} while (_markStack.Count != 0);
}

void ComputeMarkedNodes()
{
do
{
// Run mark stack algorithm as much as possible
ProcessMarkStack();

// Compute all dependencies which were not ready during the ProcessMarkStack step
ComputeDependencies(_deferredStaticDependencies);
foreach (DependencyNodeCore<DependencyContextType> node in _deferredStaticDependencies)
{
Debug.Assert(node.StaticDependenciesAreComputed);
GetStaticDependenciesImpl(node);
}

_deferredStaticDependencies.Clear();
} while (_markStack.Count != 0);

if (_resultSorter != null)
_markedNodes.Sort(_resultSorter);

_markedNodesFinal = _markedNodes.ToImmutableArray();
_markedNodes = null;
}

private bool AddToMarkStack(DependencyNodeCore<DependencyContextType> node, string reason, DependencyNodeCore<DependencyContextType> reason1, DependencyNodeCore<DependencyContextType> reason2)
{
if (_marker.MarkNode(node, reason1, reason2, reason))
{
_markStack.Push(node);
_markedNodes.Add(node);

return true;
}

return false;
}
}
}
Loading