73c89cc7991f9c770da19dce0a6fa69deb7474e4
[mono.git] / mcs / tools / linker / Mono.Linker.Steps / SweepStep.cs
1 //
2 // SweepStep.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@gmail.com)
6 //
7 // (C) 2006 Jb Evain
8 // (C) 2007 Novell, Inc.
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System.Collections;
31 using System.Collections.Generic;
32 using Mono.Cecil;
33 using Mono.Collections.Generic;
34
35 namespace Mono.Linker.Steps {
36
37         public class SweepStep : BaseStep {
38
39                 AssemblyDefinition [] assemblies;
40                 HashSet<AssemblyDefinition> resolvedTypeReferences;
41
42                 protected override void Process ()
43                 {
44                         assemblies = Context.GetAssemblies ();
45                         foreach (var assembly in assemblies) {
46                                 SweepAssembly (assembly);
47                                 if (Annotations.GetAction (assembly) == AssemblyAction.Copy) {
48                                         // Copy assemblies can still contain Type references with
49                                         // type forwarders from Delete assemblies
50                                         // thus try to resolve all the type references and see
51                                         // if some changed the scope. if yes change the action to Save
52                                         if (ResolveAllTypeReferences (assembly))
53                                                 Annotations.SetAction (assembly, AssemblyAction.Save);
54                                 }
55                         }
56                 }
57
58                 void SweepAssembly (AssemblyDefinition assembly)
59                 {
60                         if (Annotations.GetAction (assembly) != AssemblyAction.Link)
61                                 return;
62
63                         if (!IsMarkedAssembly (assembly)) {
64                                 RemoveAssembly (assembly);
65                                 return;
66                         }
67
68                         var types = new List<TypeDefinition> ();
69
70                         foreach (TypeDefinition type in assembly.MainModule.Types) {
71                                 if (Annotations.IsMarked (type)) {
72                                         SweepType (type);
73                                         types.Add (type);
74                                         continue;
75                                 }
76
77                                 if (type.Name == "<Module>")
78                                         types.Add (type);
79                         }
80
81                         assembly.MainModule.Types.Clear ();
82                         foreach (TypeDefinition type in types)
83                                 assembly.MainModule.Types.Add (type);
84                 }
85
86                 bool IsMarkedAssembly (AssemblyDefinition assembly)
87                 {
88                         return Annotations.IsMarked (assembly.MainModule);
89                 }
90
91                 void RemoveAssembly (AssemblyDefinition assembly)
92                 {
93                         Annotations.SetAction (assembly, AssemblyAction.Delete);
94
95                         SweepReferences (assembly);
96                 }
97
98                 void SweepReferences (AssemblyDefinition target)
99                 {
100                         foreach (var assembly in assemblies)
101                                 SweepReferences (assembly, target);
102                 }
103
104                 void SweepReferences (AssemblyDefinition assembly, AssemblyDefinition target)
105                 {
106                         if (assembly == target)
107                                 return;
108
109                         var references = assembly.MainModule.AssemblyReferences;
110                         for (int i = 0; i < references.Count; i++) {
111                                 var reference = references [i];
112                                 var r = Context.Resolver.Resolve (reference);
113                                 if (!AreSameReference (r.Name, target.Name))
114                                         continue;
115
116                                 references.RemoveAt (i);
117                                 // Removing the reference does not mean it will be saved back to disk!
118                                 // That depends on the AssemblyAction set for the `assembly`
119                                 switch (Annotations.GetAction (assembly)) {
120                                 case AssemblyAction.Copy:
121                                         // Copy means even if "unlinked" we still want that assembly to be saved back 
122                                         // to disk (OutputStep) without the (removed) reference
123                                         Annotations.SetAction (assembly, AssemblyAction.Save);
124                                         ResolveAllTypeReferences (assembly);
125                                         break;
126
127                                 case AssemblyAction.Save:
128                                 case AssemblyAction.Link:
129                                         ResolveAllTypeReferences (assembly);
130                                         break;
131                                 }
132                                 return;
133                         }
134                 }
135
136                 bool ResolveAllTypeReferences (AssemblyDefinition assembly)
137                 {
138                         if (resolvedTypeReferences == null)
139                                 resolvedTypeReferences = new HashSet<AssemblyDefinition> ();
140                         if (resolvedTypeReferences.Contains (assembly))
141                                 return false;
142                         resolvedTypeReferences.Add (assembly);
143
144                         var hash = new Dictionary<TypeReference,IMetadataScope> ();
145                         bool changes = false;
146
147                         foreach (TypeReference tr in assembly.MainModule.GetTypeReferences ()) {
148                                 if (hash.ContainsKey (tr))
149                                         continue;
150                                 var td = tr.Resolve ();
151                                 IMetadataScope scope = tr.Scope;
152                                 // at this stage reference might include things that can't be resolved
153                                 // and if it is (resolved) it needs to be kept only if marked (#16213)
154                                 if ((td != null) && Annotations.IsMarked (td)) {
155                                         scope = assembly.MainModule.ImportReference (td).Scope;
156                                         if (tr.Scope != scope)
157                                                 changes = true;
158                                         hash.Add (tr, scope);
159                                 }
160                         }
161                         if (assembly.MainModule.HasExportedTypes) {
162                                 foreach (var et in assembly.MainModule.ExportedTypes) {
163                                         var td = et.Resolve ();
164                                         IMetadataScope scope = et.Scope;
165                                         if ((td != null) && Annotations.IsMarked (td)) {
166                                                 scope = assembly.MainModule.ImportReference (td).Scope;
167                                                 hash.Add (td, scope);
168                                         }
169                                 }
170                         }
171
172                         // Resolve everything first before updating scopes.
173                         // If we set the scope to null, then calling Resolve() on any of its
174                         // nested types would crash.
175
176                         foreach (var e in hash) {
177                                 e.Key.Scope = e.Value;
178                         }
179
180                         return changes;
181                 }
182
183                 void SweepType (TypeDefinition type)
184                 {
185                         if (type.HasFields)
186                                 SweepCollection (type.Fields);
187
188                         if (type.HasMethods)
189                                 SweepCollection (type.Methods);
190
191                         if (type.HasNestedTypes)
192                                 SweepNestedTypes (type);
193                 }
194
195                 void SweepNestedTypes (TypeDefinition type)
196                 {
197                         for (int i = 0; i < type.NestedTypes.Count; i++) {
198                                 var nested = type.NestedTypes [i];
199                                 if (Annotations.IsMarked (nested)) {
200                                         SweepType (nested);
201                                 } else {
202                                         type.NestedTypes.RemoveAt (i--);
203                                 }
204                         }
205                 }
206
207                 void SweepCollection (IList list)
208                 {
209                         for (int i = 0; i < list.Count; i++)
210                                 if (!Annotations.IsMarked ((IMetadataTokenProvider) list [i]))
211                                         list.RemoveAt (i--);
212                 }
213
214                 static bool AreSameReference (AssemblyNameReference a, AssemblyNameReference b)
215                 {
216                         if (a == b)
217                                 return true;
218
219                         if (a.Name != b.Name)
220                                 return false;
221
222                         if (a.Version > b.Version)
223                                 return false;
224
225                         return true;
226                 }
227         }
228 }