Free cookie consent management tool by TermsFeed Policy Generator

source: branches/RemoveBackwardsCompatibility/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory-5.5.0/Analysis/AbiComparer.cs @ 14861

Last change on this file since 14861 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 8.6 KB
Line 
1//
2// ABIComparer.cs
3//
4// Author:
5//       Mike KrÃŒger <mkrueger@xamarin.com>
6//
7// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com)
8//
9// Permission is hereby granted, free of charge, to any person obtaining a copy
10// of this software and associated documentation files (the "Software"), to deal
11// in the Software without restriction, including without limitation the rights
12// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13// copies of the Software, and to permit persons to whom the Software is
14// furnished to do so, subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be included in
17// all copies or substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25// THE SOFTWARE.
26using System;
27using ICSharpCode.NRefactory.TypeSystem;
28using System.Collections.Generic;
29using System.Linq;
30
31namespace ICSharpCode.NRefactory.Analysis
32{
33  /// <summary>
34  /// Used to check the compatibility state of two compilations.
35  /// </summary>
36  public enum AbiCompatibility
37  {
38    /// <summary>
39    /// The ABI is equal
40    /// </summary>
41    Equal,
42
43    /// <summary>
44    /// Some items got added, but the ABI remains to be compatible
45    /// </summary>
46    Bigger,
47
48    /// <summary>
49    /// The ABI has changed
50    /// </summary>
51    Incompatible
52  }
53
54  [Serializable]
55  public sealed class AbiEventArgs : EventArgs
56  {
57    public string Message { get; set; }
58
59    public AbiEventArgs(string message)
60    {
61      this.Message = message;
62    }
63  }
64
65  /// <summary>
66  /// The Abi comparer checks the public API of two compilation and determines the compatibility state.
67  /// </summary>
68  public class AbiComparer
69  {
70    public bool StopOnIncompatibility {
71      get;
72      set;
73    }
74    void CheckContstraints(IType otype, ITypeParameter p1, ITypeParameter p2, ref AbiCompatibility compatibility)
75    {
76      if (p1.DirectBaseTypes.Count () != p2.DirectBaseTypes.Count () ||
77          p1.HasReferenceTypeConstraint != p2.HasReferenceTypeConstraint ||
78          p1.HasValueTypeConstraint != p2.HasValueTypeConstraint ||
79          p1.HasDefaultConstructorConstraint != p2.HasDefaultConstructorConstraint) {
80        OnIncompatibilityFound (new AbiEventArgs (string.Format (TranslateString ("Type parameter constraints of type {0} have changed."), otype.FullName)));
81        compatibility = AbiCompatibility.Incompatible;
82      }
83    }
84
85    void CheckContstraints(IMethod omethod, ITypeParameter p1, ITypeParameter p2, ref AbiCompatibility compatibility)
86    {
87      if (p1.DirectBaseTypes.Count () != p2.DirectBaseTypes.Count () ||
88          p1.HasReferenceTypeConstraint != p2.HasReferenceTypeConstraint ||
89          p1.HasValueTypeConstraint != p2.HasValueTypeConstraint ||
90          p1.HasDefaultConstructorConstraint != p2.HasDefaultConstructorConstraint) {
91        OnIncompatibilityFound (new AbiEventArgs (string.Format (TranslateString ("Type parameter constraints of method {0} have changed."), omethod.FullName)));
92        compatibility = AbiCompatibility.Incompatible;
93      }
94    }
95
96    void CheckTypes (ITypeDefinition oType, ITypeDefinition nType, ref AbiCompatibility compatibility)
97    {
98      int oldMemberCount = 0;
99      Predicate<IUnresolvedMember> pred = null;
100      if (oType.Kind == TypeKind.Class || oType.Kind == TypeKind.Struct)
101        pred = m => (m.IsPublic || m.IsProtected) && !m.IsOverride && !m.IsSynthetic;
102
103      for (int i = 0; i < oType.TypeParameterCount; i++) {
104        CheckContstraints (oType, oType.TypeParameters[i], nType.TypeParameters[i], ref compatibility);
105        if (compatibility == AbiCompatibility.Incompatible && StopOnIncompatibility)
106          return;
107      }
108
109      foreach (var member in oType.GetMembers (pred, GetMemberOptions.IgnoreInheritedMembers)) {
110        var newMember = nType.GetMembers (m => member.UnresolvedMember.Name == m.Name && m.IsPublic == member.IsPublic && m.IsProtected == member.IsProtected);
111        var equalMember = newMember.FirstOrDefault (m => SignatureComparer.Ordinal.Equals (member, m));
112        if (equalMember == null) {
113          compatibility = AbiCompatibility.Incompatible;
114          if (StopOnIncompatibility)
115            return;
116          continue;
117        }
118        var om = member as IMethod;
119        if (om != null) {
120          for (int i = 0; i < om.TypeParameters.Count; i++) {
121            CheckContstraints (om, om.TypeParameters[i], ((IMethod)equalMember).TypeParameters[i], ref compatibility);
122            if (compatibility == AbiCompatibility.Incompatible && StopOnIncompatibility)
123              return;
124          }
125        }
126
127        oldMemberCount++;
128      }
129      if (compatibility == AbiCompatibility.Bigger && oType.Kind != TypeKind.Interface)
130        return;
131      if (oldMemberCount != nType.GetMembers (pred, GetMemberOptions.IgnoreInheritedMembers).Count ()) {
132        if (oType.Kind == TypeKind.Interface) {
133          OnIncompatibilityFound (new AbiEventArgs (string.Format (TranslateString ("Interafce {0} has changed."), oType.FullName)));
134          compatibility = AbiCompatibility.Incompatible;
135        } else {
136          if (compatibility == AbiCompatibility.Equal)
137            compatibility = AbiCompatibility.Bigger;
138        }
139      }
140    }
141
142    void CheckNamespace(INamespace oNs, INamespace nNs, ref AbiCompatibility compatibility)
143    {
144      foreach (var type in oNs.Types) {
145        if (!type.IsPublic && !type.IsProtected)
146          continue;
147        var newType = nNs.GetTypeDefinition (type.Name, type.TypeParameterCount);
148        if (newType == null) {
149          OnIncompatibilityFound (new AbiEventArgs (string.Format (TranslateString ("Type definition {0} is missing."), type.FullName)));
150          compatibility = AbiCompatibility.Incompatible;
151          if (StopOnIncompatibility)
152            return;
153          continue;
154        }
155        CheckTypes (type, newType, ref compatibility);
156        if (compatibility == AbiCompatibility.Incompatible && StopOnIncompatibility)
157          return;
158      }
159
160      if (compatibility == AbiCompatibility.Bigger)
161        return;
162      foreach (var type in nNs.Types) {
163        if (!type.IsPublic && !type.IsProtected)
164          continue;
165        if (oNs.GetTypeDefinition (type.Name, type.TypeParameterCount) == null) {
166          if (compatibility == AbiCompatibility.Equal)
167            compatibility = AbiCompatibility.Bigger;
168          return;
169        }
170      }
171    }
172
173    static bool ContainsPublicTypes(INamespace testNs)
174    {
175      var stack = new Stack<INamespace> ();
176      stack.Push (testNs);
177      while (stack.Count > 0) {
178        var ns = stack.Pop ();
179        if (ns.Types.Any (t => t.IsPublic))
180          return true;
181        foreach (var child in ns.ChildNamespaces)
182          stack.Push (child);
183      }
184      return false;
185    }
186
187    /// <summary>
188    /// Check the specified oldProject and newProject if they're compatible.
189    /// </summary>
190    /// <param name="oldProject">Old project.</param>
191    /// <param name="newProject">New project.</param>
192    public AbiCompatibility Check (ICompilation oldProject, ICompilation newProject)
193    {
194      var oldStack = new Stack<INamespace> ();
195      var newStack = new Stack<INamespace> ();
196      oldStack.Push (oldProject.MainAssembly.RootNamespace);
197      newStack.Push (newProject.MainAssembly.RootNamespace);
198
199      AbiCompatibility compatibility = AbiCompatibility.Equal;
200      while (oldStack.Count > 0) {
201        var oNs = oldStack.Pop ();
202        var nNs = newStack.Pop ();
203
204        CheckNamespace (oNs, nNs, ref compatibility);
205        if (compatibility == AbiCompatibility.Incompatible && StopOnIncompatibility)
206          return AbiCompatibility.Incompatible;
207        foreach (var child in oNs.ChildNamespaces) {
208          var newChild = nNs.GetChildNamespace (child.Name);
209          if (newChild == null) {
210            OnIncompatibilityFound (new AbiEventArgs (string.Format (TranslateString ("Namespace {0} is missing."), child.FullName)));
211            if (StopOnIncompatibility)
212              return AbiCompatibility.Incompatible;
213            continue;
214          }
215          oldStack.Push (child);
216          newStack.Push (newChild);
217        }
218
219        // check if namespaces are added
220        if (compatibility != AbiCompatibility.Bigger) {
221          foreach (var child in nNs.ChildNamespaces) {
222            if (oNs.GetChildNamespace (child.Name) == null) {
223              if (compatibility == AbiCompatibility.Equal && ContainsPublicTypes (child))
224                compatibility = AbiCompatibility.Bigger;
225              break;
226            }
227          }
228        }
229      }
230      return compatibility;
231    }
232
233    public virtual string TranslateString(string str)
234    {
235      return str;
236    }
237
238    public event EventHandler<AbiEventArgs> IncompatibilityFound;
239
240    protected virtual void OnIncompatibilityFound(AbiEventArgs e)
241    {
242      var handler = IncompatibilityFound;
243      if (handler != null)
244        handler(this, e);
245    }
246  }
247}
248
Note: See TracBrowser for help on using the repository browser.