aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Link/ResolveReferencesCommand.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/Link/ResolveReferencesCommand.cs')
-rw-r--r--src/WixToolset.Core/Link/ResolveReferencesCommand.cs178
1 files changed, 178 insertions, 0 deletions
diff --git a/src/WixToolset.Core/Link/ResolveReferencesCommand.cs b/src/WixToolset.Core/Link/ResolveReferencesCommand.cs
new file mode 100644
index 00000000..5a985f3f
--- /dev/null
+++ b/src/WixToolset.Core/Link/ResolveReferencesCommand.cs
@@ -0,0 +1,178 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Link
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Linq;
9 using WixToolset.Data;
10 using WixToolset.Data.Rows;
11
12 /// <summary>
13 /// Resolves all the simple references in a section.
14 /// </summary>
15 internal class ResolveReferencesCommand : ICommand
16 {
17 private Section entrySection;
18 private IDictionary<string, Symbol> symbols;
19 private HashSet<Symbol> referencedSymbols;
20 private HashSet<Section> resolvedSections;
21
22 public ResolveReferencesCommand(Section entrySection, IDictionary<string, Symbol> symbols)
23 {
24 this.entrySection = entrySection;
25 this.symbols = symbols;
26 }
27
28 public bool BuildingMergeModule { private get; set; }
29
30 public IEnumerable<Symbol> ReferencedSymbols { get { return this.referencedSymbols; } }
31
32 public IEnumerable<Section> ResolvedSections { get { return this.resolvedSections; } }
33
34 /// <summary>
35 /// Resolves all the simple references in a section.
36 /// </summary>
37 public void Execute()
38 {
39 this.resolvedSections = new HashSet<Section>();
40 this.referencedSymbols = new HashSet<Symbol>();
41
42 this.RecursivelyResolveReferences(this.entrySection);
43 }
44
45 /// <summary>
46 /// Recursive helper function to resolve all references of passed in section.
47 /// </summary>
48 /// <param name="section">Section with references to resolve.</param>
49 /// <remarks>Note: recursive function.</remarks>
50 private void RecursivelyResolveReferences(Section section)
51 {
52 // If we already resolved this section, move on to the next.
53 if (!this.resolvedSections.Add(section))
54 {
55 return;
56 }
57
58 // Process all of the references contained in this section using the collection of
59 // symbols provided. Then recursively call this method to process the
60 // located symbol's section. All in all this is a very simple depth-first
61 // search of the references per-section.
62 Table wixSimpleReferenceTable;
63 if (section.Tables.TryGetTable("WixSimpleReference", out wixSimpleReferenceTable))
64 {
65 foreach (WixSimpleReferenceRow wixSimpleReferenceRow in wixSimpleReferenceTable.Rows)
66 {
67 Debug.Assert(wixSimpleReferenceRow.Section == section);
68
69 // If we're building a Merge Module, ignore all references to the Media table
70 // because Merge Modules don't have Media tables.
71 if (this.BuildingMergeModule && "Media" == wixSimpleReferenceRow.TableName)
72 {
73 continue;
74 }
75
76 Symbol symbol;
77 if (!this.symbols.TryGetValue(wixSimpleReferenceRow.SymbolicName, out symbol))
78 {
79 Messaging.Instance.OnMessage(WixErrors.UnresolvedReference(wixSimpleReferenceRow.SourceLineNumbers, wixSimpleReferenceRow.SymbolicName));
80 }
81 else // see if the symbol (and any of its duplicates) are appropriately accessible.
82 {
83 IList<Symbol> accessible = DetermineAccessibleSymbols(section, symbol);
84 if (!accessible.Any())
85 {
86 Messaging.Instance.OnMessage(WixErrors.UnresolvedReference(wixSimpleReferenceRow.SourceLineNumbers, wixSimpleReferenceRow.SymbolicName, symbol.Access));
87 }
88 else if (1 == accessible.Count)
89 {
90 Symbol accessibleSymbol = accessible[0];
91 this.referencedSymbols.Add(accessibleSymbol);
92
93 if (null != accessibleSymbol.Section)
94 {
95 RecursivelyResolveReferences(accessibleSymbol.Section);
96 }
97 }
98 else // display errors for the duplicate symbols.
99 {
100 Symbol accessibleSymbol = accessible[0];
101 string referencingSourceLineNumber = wixSimpleReferenceRow.SourceLineNumbers.ToString();
102 if (String.IsNullOrEmpty(referencingSourceLineNumber))
103 {
104 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol(accessibleSymbol.Row.SourceLineNumbers, accessibleSymbol.Name));
105 }
106 else
107 {
108 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol(accessibleSymbol.Row.SourceLineNumbers, accessibleSymbol.Name, referencingSourceLineNumber));
109 }
110
111 foreach (Symbol accessibleDuplicate in accessible.Skip(1))
112 {
113 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol2(accessibleDuplicate.Row.SourceLineNumbers));
114 }
115 }
116 }
117 }
118 }
119 }
120
121 /// <summary>
122 /// Determine if the symbol and any of its duplicates are accessbile by referencing section.
123 /// </summary>
124 /// <param name="referencingSection">Section referencing the symbol.</param>
125 /// <param name="symbol">Symbol being referenced.</param>
126 /// <returns>List of symbols accessible by referencing section.</returns>
127 private IList<Symbol> DetermineAccessibleSymbols(Section referencingSection, Symbol symbol)
128 {
129 List<Symbol> symbols = new List<Symbol>();
130
131 if (AccessibleSymbol(referencingSection, symbol))
132 {
133 symbols.Add(symbol);
134 }
135
136 foreach (Symbol dupe in symbol.PossiblyConflictingSymbols)
137 {
138 if (AccessibleSymbol(referencingSection, dupe))
139 {
140 symbols.Add(dupe);
141 }
142 }
143
144 foreach (Symbol dupe in symbol.RedundantSymbols)
145 {
146 if (AccessibleSymbol(referencingSection, dupe))
147 {
148 symbols.Add(dupe);
149 }
150 }
151
152 return symbols;
153 }
154
155 /// <summary>
156 /// Determine if a single symbol is accessible by the referencing section.
157 /// </summary>
158 /// <param name="referencingSection">Section referencing the symbol.</param>
159 /// <param name="symbol">Symbol being referenced.</param>
160 /// <returns>True if symbol is accessible.</returns>
161 private bool AccessibleSymbol(Section referencingSection, Symbol symbol)
162 {
163 switch (symbol.Access)
164 {
165 case AccessModifier.Public:
166 return true;
167 case AccessModifier.Internal:
168 return symbol.Row.Section.IntermediateId.Equals(referencingSection.IntermediateId) || (null != symbol.Row.Section.LibraryId && symbol.Row.Section.LibraryId.Equals(referencingSection.LibraryId));
169 case AccessModifier.Protected:
170 return symbol.Row.Section.IntermediateId.Equals(referencingSection.IntermediateId);
171 case AccessModifier.Private:
172 return referencingSection == symbol.Section;
173 default:
174 throw new InvalidOperationException();
175 }
176 }
177 }
178}