Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory-5.5.0/Completion/FrameworkLookup.cs @ 15737

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

#2077: created branch and added first version

File size: 16.8 KB
Line 
1//
2// FrameworkLookup.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 System.Collections.Generic;
28using System.IO;
29using System.Linq;
30using System.Text;
31using ICSharpCode.NRefactory.Semantics;
32using ICSharpCode.NRefactory.TypeSystem;
33using ICSharpCode.NRefactory.TypeSystem.Implementation;
34
35namespace ICSharpCode.NRefactory.Completion
36{
37  /// <summary>
38  /// The framework lookup provides a fast lookup where an unknow type or extension method may be defined in.
39  /// </summary>
40  public sealed class FrameworkLookup
41  {
42    /*     Binary format:
43     * [Header]
44     *    [Version] : [Major (byte)] [Minor (byte)] [Build  (byte)]
45     *    [#Types (int)]
46     *    [#Methods (int)]
47     *    [#Assemblies (int)]
48     * [AssemblyListTable] : #Assemblies x [OffsetToAssemblyLists (int)]
49     * [TypeLookupTable] : #Types x ( [NameHash (int)] [AssemblyPtrToAssemblyListTable (ushort)]
50     * [ExtMethodLookupTable] : #Methods x ( [NameHash (int)] [AssemblyPtrToAssemblyListTable (ushort)]
51     * [AssemblyLists]
52     *    [#Count (byte)]
53     *    #Count x [AssemblyLookup] : [Package (string)] [FullName (string)] [Namespace (string)]
54    */
55    const int headerSize =
56      3 + // Version
57      4 + // #Types
58      4 + // #Methods
59      4   // #Assembly
60      /*      + 4*/;
61
62    public static readonly Version CurrentVersion = new Version (2, 0, 1);
63    public static readonly FrameworkLookup Empty = new FrameworkLookup ();
64
65    string fileName;
66    int[] assemblyListTable;
67    int[] typeLookupTable;
68    int[] extLookupTable;
69
70    /// <summary>
71    /// This method tries to get a matching extension method.
72    /// </summary>
73    /// <returns>The extension method lookups.</returns>
74    /// <param name="resolveResult">The resolve result.</param>
75    public IEnumerable<AssemblyLookup> GetExtensionMethodLookups (UnknownMemberResolveResult resolveResult)
76    {
77      return GetLookup (resolveResult.MemberName, extLookupTable, headerSize + assemblyListTable.Length * 4 + typeLookupTable.Length * 8);
78    }
79
80    /// <summary>
81    /// Tries to get a type out of an unknow identifier result.
82    /// </summary>
83    /// <returns>The assemblies the type may be defined (if any).</returns>
84    /// <param name="resolveResult">The resolve result.</param>
85    /// <param name="typeParameterCount">Type parameter count.</param>
86    /// <param name="isInsideAttributeType">If set to <c>true</c> this resolve result may be inside an attribute.</param>
87    public IEnumerable<AssemblyLookup> GetLookups (UnknownIdentifierResolveResult resolveResult, int typeParameterCount, bool isInsideAttributeType)
88    {
89      string name = isInsideAttributeType ? resolveResult.Identifier + "Attribute" : resolveResult.Identifier;
90
91      var identifier = GetIdentifier (name, typeParameterCount);
92      return GetLookup (identifier, typeLookupTable, headerSize + assemblyListTable.Length * 4);
93    }
94
95    /// <summary>
96    /// Tries to get a type out of an unknow member resolve result. (In case of fully qualified names)
97    /// </summary>
98    /// <returns>The assemblies the type may be defined (if any).</returns>
99    /// <param name="resolveResult">The resolve result.</param>
100    /// <param name="fullMemberName"></param>
101    /// <param name="typeParameterCount">Type parameter count.</param>
102    /// <param name="isInsideAttributeType">If set to <c>true</c> this resolve result may be inside an attribute.</param>
103    public IEnumerable<AssemblyLookup> GetLookups (UnknownMemberResolveResult resolveResult, string fullMemberName, int typeParameterCount, bool isInsideAttributeType)
104    {
105      string name = isInsideAttributeType ? resolveResult.MemberName + "Attribute" : resolveResult.MemberName;
106
107      var identifier = GetIdentifier (name, typeParameterCount);
108      foreach (var lookup in GetLookup (identifier, typeLookupTable, headerSize + assemblyListTable.Length * 4)) {
109        if (fullMemberName.StartsWith (lookup.Namespace, StringComparison.Ordinal))
110          yield return lookup;
111      }
112    }
113
114    /// <summary>
115    /// The assembly lookup determines where a type might be defined.
116    /// It contains the assembly &amp; the namespace.
117    /// </summary>
118    public struct AssemblyLookup
119    {
120      readonly string nspace;
121
122      /// <summary>
123      /// The namespace the requested type is in.
124      /// </summary>
125      public string Namespace {
126        get {
127          return nspace;
128        }
129      }
130
131      readonly string fullName;
132      /// <summary>
133      /// Gets the full name af the assembly.
134      /// </summary>
135      public string FullName {
136        get {
137          return fullName;
138        }
139      }
140
141      readonly string package;
142      /// <summary>
143      /// Gets the package the assembly is in.
144      /// </summary>
145      public string Package {
146        get {
147          return package;
148        }
149      }
150
151      /// <summary>
152      /// Initializes a new instance of the <see cref="AssemblyLookup"/> struct.
153      /// </summary>
154      /// <param name="package">The package name.</param>
155      /// <param name="fullName">The full name of the assembly.</param>
156      /// <param name="nspace">The namespace the type is in.</param>
157      internal AssemblyLookup (string package, string fullName, string nspace)
158      {
159        if (nspace == null)
160          throw new ArgumentNullException ("nspace");
161        if (fullName == null)
162          throw new ArgumentNullException ("fullName");
163        this.package = package;
164        this.fullName = fullName;
165        this.nspace = nspace;
166      }
167
168      public override string ToString ()
169      {
170        return string.Format ("[AssemblyLookup: Namespace={0}, FullName={1}, Package={2}]", Namespace, FullName, Package);
171      }
172
173      public override bool Equals (object obj)
174      {
175        if (obj == null)
176          return false;
177        //          if (ReferenceEquals (this, obj))
178        //            return true;
179        if (obj.GetType () != typeof(AssemblyLookup))
180          return false;
181        var other = (AssemblyLookup)obj;
182        return Namespace == other.Namespace && FullName == other.FullName && Package == other.Package;
183      }
184
185      public override int GetHashCode ()
186      {
187        unchecked {
188          return (Namespace != null ? Namespace.GetHashCode () : 0) ^
189            (FullName != null ? FullName.GetHashCode () : 0) ^
190            (Package != null ? Package.GetHashCode () : 0);
191        }
192      }
193    }
194
195    /// <summary>
196    /// This method returns a new framework builder to build a new framework lookup data file.
197    /// </summary>
198    /// <param name="fileName">The file name of the data file.</param>
199    public static FrameworkBuilder Create (string fileName)
200    {
201      return new FrameworkBuilder (fileName);
202    }
203
204    /// <summary>
205    /// Loads a framework lookup object from a file. May return null, if the file wasn't found or has a version mismatch.
206    /// </summary>
207    /// <param name="fileName">File name.</param>
208    public static FrameworkLookup Load (string fileName)
209    {
210      try {
211        if (!File.Exists (fileName))
212          return null;
213      } catch (Exception) {
214        return null;
215      }
216      var result = new FrameworkLookup ();
217      result.fileName = fileName;
218      var fs = File.OpenRead (fileName);
219      using (var reader = new BinaryReader (fs, Encoding.UTF8)) {
220        var major = reader.ReadByte ();
221        var minor = reader.ReadByte ();
222        var build = reader.ReadByte ();
223        var version = new Version (major, minor, build);
224        if (version != CurrentVersion)
225          return null;
226        int typeLookupListCount = reader.ReadInt32 ();
227        int extLookupListCount = reader.ReadInt32 ();
228        int assemblyLookupCount = reader.ReadInt32 ();
229
230        result.assemblyListTable = new int[assemblyLookupCount];
231        for (int i = 0; i < assemblyLookupCount; i++) {
232          result.assemblyListTable[i] = reader.ReadInt32 ();
233        }
234
235        result.typeLookupTable = new int[typeLookupListCount];
236        for (int i = 0; i < typeLookupListCount; i++) {
237          result.typeLookupTable [i] = reader.ReadInt32 ();
238          // skip list offset
239          reader.ReadInt32 ();
240        }
241
242        result.extLookupTable = new int[extLookupListCount];
243        for (int i = 0; i < extLookupListCount; i++) {
244          result.extLookupTable [i] = reader.ReadInt32 ();
245          // skip list offset
246          reader.ReadInt32 ();
247        }
248      }
249      return result;
250    }
251
252    FrameworkLookup ()
253    {
254    }
255
256    IEnumerable<AssemblyLookup> GetLookup (string identifier, int[] lookupTable, int tableOffset)
257    {
258      if (lookupTable == null)
259        yield break;
260
261      int index = Array.BinarySearch (lookupTable, GetStableHashCode (identifier));
262      if (index < 0)
263        yield break;
264
265      using (var reader = new BinaryReader (File.Open (fileName, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)) {
266        reader.BaseStream.Seek (tableOffset + index * 8 + 4, SeekOrigin.Begin);
267        int listPtr = reader.ReadInt32 ();
268
269        reader.BaseStream.Seek (listPtr, SeekOrigin.Begin);
270        var b = reader.ReadInt32 ();
271        var assemblies = new List<ushort> ();
272        while (b-- > 0) {
273          var assembly = reader.ReadUInt16 ();
274          if (assembly < 0 || assembly >= assemblyListTable.Length)
275            throw new InvalidDataException ("Assembly lookup was " + assembly + " but only " + assemblyListTable.Length + " are known.");
276          assemblies.Add (assembly);
277        }
278        foreach (var assembly in assemblies) {
279          reader.BaseStream.Seek (assemblyListTable [assembly], SeekOrigin.Begin);
280
281          var package = reader.ReadString ();
282          var fullName = reader.ReadString ();
283          var ns = reader.ReadString ();
284          yield return new AssemblyLookup (package, fullName, ns);
285        }
286      }
287    }
288
289    /// <summary>
290    /// Retrieves a hash code for the specified string that is stable across
291    /// .NET upgrades.
292    ///
293    /// Use this method instead of the normal <c>string.GetHashCode</c> if the hash code
294    /// is persisted to disk.
295    /// </summary>
296    static int GetStableHashCode(string text)
297    {
298      unchecked {
299        int h = 0;
300        foreach (char c in text) {
301          h = (h << 5) - h + c;
302        }
303        return h;
304      }
305    }
306
307    static string GetIdentifier (string identifier, int tc)
308    {
309      if (tc == 0)
310        return identifier;
311      return identifier + "`" + tc;
312    }
313
314    public class FrameworkBuilder : IDisposable
315    {
316      readonly string fileName;
317
318      Dictionary<int, List<ushort>> typeLookup = new Dictionary<int, List<ushort>>  ();
319      Dictionary<int, List<ushort>> extensionMethodLookup = new Dictionary<int, List<ushort>>  ();
320      List<AssemblyLookup> assemblyLookups = new List<AssemblyLookup> ();
321      Dictionary<int, string> methodCheck = new Dictionary<int, string> ();
322      Dictionary<int, string> typeCheck = new Dictionary<int, string> ();
323
324      internal FrameworkBuilder (string fileName)
325      {
326        this.fileName = fileName;
327      }
328
329      static int[] WriteTable (MemoryStream stream, Dictionary<int, List<ushort>> table, out List<KeyValuePair<int, List<ushort>>> list)
330      {
331        list = new List<KeyValuePair<int, List<ushort>>> (table);
332        list.Sort ((x, y) => x.Key.CompareTo (y.Key));
333
334        var result = new int[list.Count];
335        using (var bw = new BinaryWriter (stream)) {
336          for (int i = 0; i < result.Length; i++) {
337            result [i] = (int)stream.Length;
338            bw.Write (list [i].Value.Count);
339            foreach (var ii in list [i].Value)
340              bw.Write (ii);
341          }
342        }
343
344        return result;
345      }
346
347      #region IDisposable implementation
348
349      void IDisposable.Dispose ()
350      {
351        var typeLookupMemory = new MemoryStream ();
352        List<KeyValuePair<int, List<ushort>>> typeLookupList;
353        var typeTable = WriteTable (typeLookupMemory, typeLookup, out typeLookupList);
354
355        var extMethodLookupMemory = new MemoryStream ();
356        List<KeyValuePair<int, List<ushort>>> extMethodLookuplist;
357        var extMethodTable = WriteTable (extMethodLookupMemory, extensionMethodLookup, out extMethodLookuplist);
358
359        var assemblyLookupMemory = new MemoryStream ();
360        var assemblyPositionTable = new int[assemblyLookups.Count];
361        using (var writer = new BinaryWriter (assemblyLookupMemory, Encoding.UTF8)) {
362          for (int i = 0; i < assemblyLookups.Count; i++) {
363            var lookup = assemblyLookups[i];
364            assemblyPositionTable[i] = (int)assemblyLookupMemory.Length;
365            writer.Write (lookup.Package);
366            writer.Write (lookup.FullName);
367            writer.Write (lookup.Namespace);
368          }
369        }
370
371        using (var stream = new BinaryWriter (File.OpenWrite (fileName), Encoding.UTF8)) {
372          stream.Write ((byte)CurrentVersion.Major);
373          stream.Write ((byte)CurrentVersion.Minor);
374          stream.Write ((byte)CurrentVersion.Build);
375
376          stream.Write (typeLookupList.Count);
377          stream.Write (extMethodLookuplist.Count);
378          stream.Write (assemblyLookups.Count);
379
380          var typeBuffer = typeLookupMemory.ToArray ();
381          var extMethodBuffer = extMethodLookupMemory.ToArray ();
382
383          int dataOffset =
384            headerSize +
385            assemblyLookups.Count * 4 +
386            typeLookupList.Count * (4 + 4) +
387            extMethodLookuplist.Count * (4 + 4);
388
389          for (int i = 0; i < assemblyLookups.Count; i++) {
390            stream.Write ((int)(dataOffset + typeBuffer.Length + extMethodBuffer.Length + assemblyPositionTable[i]));
391          }
392
393          for (int i = 0; i < typeLookupList.Count; i++) {
394            stream.Write (typeLookupList [i].Key);
395            stream.Write (dataOffset + typeTable[i]);
396          }
397
398          for (int i = 0; i < extMethodLookuplist.Count; i++) {
399            stream.Write (extMethodLookuplist [i].Key);
400            stream.Write (dataOffset + typeBuffer.Length + extMethodTable[i]);
401          }
402
403          stream.Write (typeBuffer);
404          stream.Write (extMethodBuffer);
405          stream.Write (assemblyLookupMemory.ToArray ());
406          stream.Flush ();
407        }
408      }
409      #endregion
410
411      struct FrameworkLookupId
412      {
413        public string PackageName;
414        public string AssemblyName;
415        public string NameSpace;
416      }
417
418      Dictionary<FrameworkLookupId, ushort> frameworkLookupTable = new Dictionary<FrameworkLookupId, ushort> ();
419      ushort GetLookup (string packageName, string assemblyName, string ns)
420      {
421        var id = new FrameworkLookupId {
422          PackageName = packageName,
423          AssemblyName = assemblyName,
424          NameSpace = ns
425        };
426        ushort value;
427        if (frameworkLookupTable.TryGetValue (id, out value))
428          return value;
429
430        var result = new AssemblyLookup (packageName, assemblyName, ns);
431        assemblyLookups.Add (result);
432        var index = assemblyLookups.Count - 1;
433        if (index > ushort.MaxValue)
434          throw new InvalidOperationException ("Assembly lookup list overflow > " + ushort.MaxValue + " assemblies.");
435        frameworkLookupTable.Add (id, (ushort)index);
436        return (ushort)index;
437      }
438
439      bool AddToTable (string packageName, string assemblyName, Dictionary<int, List<ushort>> table, Dictionary<int, string> checkTable, string id, string ns)
440      {
441        List<ushort> list;
442        var hash = GetStableHashCode (id);
443
444        if (!table.TryGetValue (hash, out list)) {
445          list = new List<ushort> ();
446          table [hash] = list;
447        } else {
448          string existingString;
449          if (checkTable.TryGetValue (hash, out existingString)) {
450            if (existingString != id)
451              throw new InvalidOperationException ("Duplicate hash for " + existingString + " and "+ id);
452          } else {
453            checkTable.Add (hash, id);
454          }
455        }
456        var assemblyLookup = GetLookup (packageName, assemblyName, ns);
457        if (!list.Any (a => a.Equals (assemblyLookup))) {
458          list.Add (assemblyLookup);
459          return true;
460        }
461        return false;
462      }
463
464      /// <summary>
465      /// Add a type to the framework lookup.
466      /// </summary>
467      /// <param name="packageName">The package the assembly of the type is defined (can be null).</param>
468      /// <param name="fullAssemblyName">The full assembly name the type is defined (needs to be != null).</param>
469      /// <param name="type">The type definition  (needs to be != null).</param>
470      public void AddLookup (string packageName, string fullAssemblyName, IUnresolvedTypeDefinition type)
471      {
472        if (fullAssemblyName == null)
473          throw new ArgumentNullException ("fullAssemblyName");
474        if (type == null)
475          throw new ArgumentNullException ("type");
476        var id = GetIdentifier (type.Name, type.TypeParameters.Count);
477        if (AddToTable (packageName, fullAssemblyName, typeLookup, typeCheck, id, type.Namespace)) {
478          if (type.IsSealed || type.IsStatic) {
479            foreach (var method in type.Methods) {
480              var m = method as DefaultUnresolvedMethod;
481              if (m == null || !m.IsExtensionMethod)
482                continue;
483              AddToTable (packageName, fullAssemblyName, extensionMethodLookup, methodCheck, method.Name, method.DeclaringTypeDefinition.Namespace);
484            }
485          }
486        }
487      }
488    }
489  }
490}
Note: See TracBrowser for help on using the repository browser.