Free cookie consent management tool by TermsFeed Policy Generator

source: branches/CodeEditor/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Parser/mcs/membercache.cs @ 11700

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

#2077: created branch and added first version

File size: 42.8 KB
Line 
1//
2// membercache.cs: A container for all member lookups
3//
4// Author: Miguel de Icaza (miguel@gnu.org)
5//         Marek Safar (marek.safar@gmail.com)
6//
7// Dual licensed under the terms of the MIT X11 or GNU GPL
8//
9// Copyright 2001 Ximian, Inc (http://www.ximian.com)
10// Copyright 2004-2010 Novell, Inc
11// Copyright 2011 Xamarin Inc
12//
13//
14
15using System;
16using System.Collections.Generic;
17
18namespace Mono.CSharp {
19
20  [Flags]
21  public enum MemberKind
22  {
23    Constructor = 1,
24    Event = 1 << 1,
25    Field = 1 << 2,
26    Method = 1 << 3,
27    Property = 1 << 4,
28    Indexer = 1 << 5,
29    Operator = 1 << 6,
30    Destructor  = 1 << 7,
31
32    Class   = 1 << 11,
33    Struct    = 1 << 12,
34    Delegate  = 1 << 13,
35    Enum    = 1 << 14,
36    Interface = 1 << 15,
37    TypeParameter = 1 << 16,
38
39    ArrayType = 1 << 19,
40    PointerType = 1 << 20,
41    InternalCompilerType = 1 << 21,
42    MissingType = 1 << 22,
43    Void = 1 << 23,
44    Namespace = 1 << 24,
45
46    NestedMask = Class | Struct | Delegate | Enum | Interface,
47    GenericMask = Method | Class | Struct | Delegate | Interface,
48    MaskType = Constructor | Event | Field | Method | Property | Indexer | Operator | Destructor | NestedMask
49  }
50
51  [Flags]
52  public enum BindingRestriction
53  {
54    None = 0,
55
56    // Inspect only queried type members
57    DeclaredOnly = 1 << 1,
58
59    // Exclude static
60    InstanceOnly = 1 << 2,
61
62    NoAccessors = 1 << 3,
63
64    // Member has to be override
65    OverrideOnly = 1 << 4
66  }
67
68  public struct MemberFilter : IEquatable<MemberSpec>
69  {
70    public readonly string Name;
71    public readonly MemberKind Kind;
72    public readonly AParametersCollection Parameters;
73    public readonly TypeSpec MemberType;
74    public readonly int Arity; // -1 to ignore the check
75
76    public MemberFilter (MethodSpec m)
77    {
78      Name = m.Name;
79      Kind = MemberKind.Method;
80      Parameters = m.Parameters;
81      MemberType = m.ReturnType;
82      Arity = m.Arity;
83    }
84
85    public MemberFilter (string name, int arity, MemberKind kind, AParametersCollection param, TypeSpec type)
86    {
87      Name = name;
88      Kind = kind;
89      Parameters = param;
90      MemberType = type;
91      this.Arity = arity;
92    }
93
94    public static MemberFilter Constructor (AParametersCollection param)
95    {
96      return new MemberFilter (Mono.CSharp.Constructor.ConstructorName, 0, MemberKind.Constructor, param, null);
97    }
98
99    public static MemberFilter Property (string name, TypeSpec type)
100    {
101      return new MemberFilter (name, 0, MemberKind.Property, null, type);
102    }
103
104    public static MemberFilter Field (string name, TypeSpec type)
105    {
106      return new MemberFilter (name, 0, MemberKind.Field, null, type);
107    }
108
109    public static MemberFilter Method (string name, int arity, AParametersCollection param, TypeSpec type)
110    {
111      return new MemberFilter (name, arity, MemberKind.Method, param, type);
112    }
113
114    #region IEquatable<MemberSpec> Members
115
116    public bool Equals (MemberSpec other)
117    {
118      // Is the member of the correct type ?
119      // TODO: Isn't this redundant ?
120      if ((other.Kind & Kind & MemberKind.MaskType) == 0)
121        return false;
122
123      // Check arity when not disabled
124      if (Arity >= 0 && Arity != other.Arity)
125        return false;
126
127      if (Parameters != null) {
128        if (other is IParametersMember) {
129          var other_param = ((IParametersMember) other).Parameters;
130          if (!TypeSpecComparer.Override.IsEqual (Parameters, other_param))
131            return false;
132        } else {
133          return false;
134        }
135      }
136
137      if (MemberType != null) {
138        if (other is IInterfaceMemberSpec) {
139          var other_type = ((IInterfaceMemberSpec) other).MemberType;
140          if (!TypeSpecComparer.Override.IsEqual (other_type, MemberType))
141            return false;
142        } else {
143          return false;
144        }
145      }
146
147      return true;
148    }
149
150    #endregion
151  }
152
153  //
154  // The MemberCache is the main members container used by compiler. It contains
155  // all members imported or defined during compilation using on demand filling
156  // process. Inflated containers are also using MemberCache to make inflated
157  // members look like normal definition.
158  //
159  // All of the methods are performance and memory sensitive as the MemberCache
160  // is the underlying engine of all member based operations.
161  //
162  public class MemberCache
163  {
164    [Flags]
165    enum StateFlags
166    {
167      HasConversionOperator = 1 << 1,
168      HasUserOperator = 1 << 2
169    }
170
171    readonly Dictionary<string, IList<MemberSpec>> member_hash;
172    Dictionary<string, MemberSpec[]> locase_members;
173    IList<MethodSpec> missing_abstract;
174    StateFlags state; // TODO: Move to TypeSpec or ITypeDefinition
175
176    public static readonly string IndexerNameAlias = "<this>";
177
178    public static readonly MemberCache Empty = new MemberCache (0);
179
180    public MemberCache ()
181      : this (16)
182    {
183    }
184
185    public MemberCache (int capacity)
186    {
187      member_hash = new Dictionary<string, IList<MemberSpec>> (capacity);
188    }
189
190    public MemberCache (MemberCache cache)
191      : this (cache.member_hash.Count)
192    {
193      this.state = cache.state;
194    }
195
196    //
197    // Creates a new MemberCache for the given `container'.
198    //
199    public MemberCache (TypeContainer container)
200      : this ()       // TODO: Optimize the size
201    {
202    }
203
204    //
205    // For cases where we need to union cache members
206    //
207    public void AddBaseType (TypeSpec baseType)
208    {
209      var cache = baseType.MemberCache;
210
211      IList<MemberSpec> list;
212      foreach (var entry in cache.member_hash) {
213        if (!member_hash.TryGetValue (entry.Key, out list)) {
214          if (entry.Value.Count == 1) {
215            list = entry.Value;
216          } else {
217            list = new List<MemberSpec> (entry.Value);
218          }
219
220          member_hash.Add (entry.Key, list);
221          continue;
222        }
223
224        foreach (var ce in entry.Value) {
225          if (list.Contains (ce))
226            continue;
227
228          if (list is MemberSpec[]) {
229            list = new List<MemberSpec> { list [0] };
230            member_hash[entry.Key] = list;
231          }
232
233          list.Add (ce);
234        }
235      }
236    }
237
238    //
239    // Member-cache does not contain base members but it does
240    // contain all base interface members, so the Lookup code
241    // can use simple inheritance rules.
242    //
243    // Does not work recursively because of generic interfaces
244    //
245    public void AddInterface (TypeSpec iface)
246    {
247      var cache = iface.MemberCache;
248
249      IList<MemberSpec> list;
250      foreach (var entry in cache.member_hash) {
251        if (!member_hash.TryGetValue (entry.Key, out list)) {
252          if (entry.Value.Count == 1) {
253            list = entry.Value;
254          } else {
255            list = new List<MemberSpec> (entry.Value);
256          }
257
258          member_hash.Add (entry.Key, list);
259          continue;
260        }
261
262        foreach (var ce in entry.Value) {
263          //
264          // When two or more different base interfaces implemenent common
265          // interface
266          //
267          // I : IA, IFoo
268          // IA : IFoo
269          //
270          if (list.Contains (ce))
271            continue;
272
273          if (AddInterfaceMember (ce, ref list))
274            member_hash[entry.Key] = list;
275        }
276      }
277    }
278
279    public void AddMember (InterfaceMemberBase imb, string exlicitName, MemberSpec ms)
280    {
281      // Explicit names cannot be looked-up but can be used for
282      // collision checking (no name mangling needed)
283      if (imb.IsExplicitImpl)
284        AddMember (exlicitName, ms, false);
285      else
286        AddMember (ms);
287    }
288
289    //
290    // Add non-explicit member to member cache
291    //
292    public void AddMember (MemberSpec ms)
293    {
294      AddMember (GetLookupName (ms), ms, false);
295    }
296
297    void AddMember (string name, MemberSpec member, bool removeHiddenMembers)
298    {
299      if (member.Kind == MemberKind.Operator) {
300        var dt = member.DeclaringType;
301
302
303        //
304        // Some core types have user operators but they cannot be used like normal
305        // user operators as they are predefined and therefore having different
306        // rules (e.g. binary operators) by not setting the flag we hide them for
307        // user conversions
308        //
309        if (!BuiltinTypeSpec.IsPrimitiveType (dt) || dt.BuiltinType == BuiltinTypeSpec.Type.Char) {
310          switch (dt.BuiltinType) {
311          case BuiltinTypeSpec.Type.String:
312          case BuiltinTypeSpec.Type.Delegate:
313          case BuiltinTypeSpec.Type.MulticastDelegate:
314            break;
315          default:
316            if (name == Operator.GetMetadataName (Operator.OpType.Implicit) || name == Operator.GetMetadataName (Operator.OpType.Explicit)) {
317              state |= StateFlags.HasConversionOperator;
318            } else {
319              state |= StateFlags.HasUserOperator;
320            }
321
322            break;
323          }
324        }
325      }
326
327      IList<MemberSpec> list;
328      if (!member_hash.TryGetValue (name, out list)) {
329        member_hash.Add (name, new MemberSpec[] { member });
330        return;
331      }
332
333      if (removeHiddenMembers && member.DeclaringType.IsInterface) {
334        if (AddInterfaceMember (member, ref list))
335          member_hash[name] = list;
336      } else {
337        if (list.Count == 1) {
338          list = new List<MemberSpec> { list[0] };
339          member_hash[name] = list;
340        }
341
342        list.Add (member);
343      }
344    }
345
346    public void AddMemberImported (MemberSpec ms)
347    {
348      AddMember (GetLookupName (ms), ms, true);
349    }
350
351    //
352    // Ignores any base interface member which can be hidden
353    // by this interface
354    //
355    static bool AddInterfaceMember (MemberSpec member, ref IList<MemberSpec> existing)
356    {
357      var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : ParametersCompiled.EmptyReadOnlyParameters;
358
359      //
360      // interface IA : IB { int Prop { set; } }
361      // interface IB { bool Prop { get; } }
362      //
363      // IB.Prop is never accessible from IA interface
364      //
365      for (int i = 0; i < existing.Count; ++i) {
366        var entry = existing[i];
367
368        if (entry.Arity != member.Arity)
369          continue;
370
371        if (entry is IParametersMember) {
372          var entry_param = ((IParametersMember) entry).Parameters;
373          if (!TypeSpecComparer.Override.IsEqual (entry_param, member_param))
374            continue;
375        }
376
377        if (member.DeclaringType.ImplementsInterface (entry.DeclaringType, false)) {
378          if (existing.Count == 1) {
379            existing = new MemberSpec[] { member };
380            return true;
381          }
382
383          existing.RemoveAt (i--);
384          continue;
385        }
386
387        if ((entry.DeclaringType == member.DeclaringType && entry.IsAccessor == member.IsAccessor) ||
388          entry.DeclaringType.ImplementsInterface (member.DeclaringType, false))
389          return false;
390      }
391
392      if (existing.Count == 1) {
393        existing = new List<MemberSpec> { existing[0], member };
394        return true;
395      }
396
397      existing.Add (member);
398      return false;
399    }
400
401    public static MemberSpec FindMember (TypeSpec container, MemberFilter filter, BindingRestriction restrictions)
402    {
403      do {
404        IList<MemberSpec> applicable;
405        if (container.MemberCache.member_hash.TryGetValue (filter.Name, out applicable)) {
406          // Start from the end because interface members are in reverse order
407          for (int i = applicable.Count - 1; i >= 0; i--) {
408            var entry = applicable [i];
409
410            if ((restrictions & BindingRestriction.InstanceOnly) != 0 && entry.IsStatic)
411              continue;
412
413            if ((restrictions & BindingRestriction.NoAccessors) != 0 && entry.IsAccessor)
414              continue;
415
416            if ((restrictions & BindingRestriction.OverrideOnly) != 0 && (entry.Modifiers & Modifiers.OVERRIDE) == 0)
417              continue;
418
419            if (!filter.Equals (entry))
420              continue;
421
422            if ((restrictions & BindingRestriction.DeclaredOnly) != 0 && container.IsInterface && entry.DeclaringType != container)
423              continue;
424
425            return entry;
426          }
427        }
428
429        if ((restrictions & BindingRestriction.DeclaredOnly) != 0)
430          break;
431
432        container = container.BaseType;
433      } while (container != null);
434
435      return null;
436    }
437
438    //
439    // A special method to work with member lookup only. It returns a list of all members named @name
440    // starting from @container. It's very performance sensitive
441    //
442    // declaredOnlyClass cannot be used interfaces. Manual filtering is required because names are
443    // compacted
444    //
445    public static IList<MemberSpec> FindMembers (TypeSpec container, string name, bool declaredOnlyClass)
446    {
447      IList<MemberSpec> applicable;
448
449      do {
450        if (container.MemberCache.member_hash.TryGetValue (name, out applicable) || declaredOnlyClass)
451          return applicable;
452
453        container = container.BaseType;
454      } while (container != null);
455
456      return null;
457    }
458
459    //
460    // Finds the nested type in container
461    //
462    public static TypeSpec FindNestedType (TypeSpec container, string name, int arity)
463    {
464      IList<MemberSpec> applicable;
465      TypeSpec best_match = null;
466      do {
467#if !FULL_AOT_RUNTIME
468        // TODO: Don't know how to handle this yet
469        // When resolving base type of nested type, parent type must have
470        // base type resolved to scan full hierarchy correctly
471        // Similarly MemberCacheTypes will inflate BaseType and Interfaces
472        // based on type definition
473        var tc = container.MemberDefinition as TypeContainer;
474        if (tc != null)
475          tc.DefineContainer ();
476#endif
477
478        if (container.MemberCacheTypes.member_hash.TryGetValue (name, out applicable)) {
479          for (int i = applicable.Count - 1; i >= 0; i--) {
480            var entry = applicable[i];
481            if ((entry.Kind & MemberKind.NestedMask) == 0)
482              continue;
483
484            var ts = (TypeSpec) entry;
485            if (arity == ts.Arity)
486              return ts;
487
488            if (arity < 0) {
489              if (best_match == null) {
490                best_match = ts;
491              } else if (System.Math.Abs (ts.Arity + arity) < System.Math.Abs (ts.Arity + arity)) {
492                best_match = ts;
493              }
494            }
495          }
496        }
497
498        container = container.BaseType;
499      } while (container != null);
500
501      return best_match;
502    }
503
504    //
505    // Looks for extension methods with defined name and extension type
506    //
507    public List<MethodSpec> FindExtensionMethods (IMemberContext invocationContext, string name, int arity)
508    {
509      IList<MemberSpec> entries;
510      if (!member_hash.TryGetValue (name, out entries))
511        return null;
512
513      List<MethodSpec> candidates = null;
514      foreach (var entry in entries) {
515        if (entry.Kind != MemberKind.Method || (arity > 0 && entry.Arity != arity))
516          continue;
517
518        var ms = (MethodSpec) entry;
519        if (!ms.IsExtensionMethod)
520          continue;
521
522        if (!ms.IsAccessible (invocationContext))
523          continue;
524
525        //
526        // Extension methods cannot be nested hence checking parent is enough
527        //
528        if ((ms.DeclaringType.Modifiers & Modifiers.INTERNAL) != 0 && !ms.DeclaringType.MemberDefinition.IsInternalAsPublic (invocationContext.Module.DeclaringAssembly))
529          continue;
530
531        if (candidates == null)
532          candidates = new List<MethodSpec> ();
533        candidates.Add (ms);
534      }
535
536      return candidates;
537    }
538
539    //
540    // Returns base members of @member member if no exact match is found @bestCandidate returns
541    // the best match
542    //
543    public static MemberSpec FindBaseMember (MemberCore member, out MemberSpec bestCandidate, ref bool overrides)
544    {
545      bestCandidate = null;
546      var container = member.Parent.PartialContainer.Definition;
547      if (!container.IsInterface) {
548        container = container.BaseType;
549
550        // It can happen for a user definition of System.Object
551        if (container == null)
552          return null;
553      }
554
555      string name = GetLookupName (member);
556      var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : null;
557
558      var mkind = GetMemberCoreKind (member);
559      bool member_with_accessors = mkind == MemberKind.Indexer || mkind == MemberKind.Property;
560
561      IList<MemberSpec> applicable;
562      MemberSpec ambig_candidate = null;
563
564      do {
565        if (container.MemberCache.member_hash.TryGetValue (name, out applicable)) {
566          for (int i = 0; i < applicable.Count; ++i) {
567            var entry = applicable [i];
568
569            if ((entry.Modifiers & Modifiers.PUBLIC) == 0 && !entry.IsAccessible (member))
570              continue;
571
572            //
573            // Isn't the member of same kind ?
574            //
575            if ((entry.Kind & ~MemberKind.Destructor & mkind & MemberKind.MaskType) == 0) {
576              // Destructors are ignored as they cannot be overridden by user
577              if ((entry.Kind & MemberKind.Destructor) != 0)
578                continue;
579
580              // A method with different arity does not hide base member
581              if (mkind != MemberKind.Method && member.MemberName.Arity != entry.Arity)
582                continue;
583
584              bestCandidate = entry;
585              return null;
586            }
587
588            //
589            // Same kind of different arity is valid
590            //
591            if (member.MemberName.Arity != entry.Arity) {
592              continue;
593            }
594
595            if ((entry.Kind & mkind & (MemberKind.Method | MemberKind.Indexer)) != 0) {
596              if (entry.IsAccessor != member is AbstractPropertyEventMethod)
597                continue;
598
599              var pm = entry as IParametersMember;
600              if (!TypeSpecComparer.Override.IsEqual (pm.Parameters, member_param))
601                continue;
602            }
603
604            //
605            // Skip override for member with accessors. It may not fully implement the base member
606            // but keep flag we found an implementation in case the base member is abstract
607            //
608            if (member_with_accessors && ((entry.Modifiers & (Modifiers.OVERRIDE | Modifiers.SEALED)) == Modifiers.OVERRIDE)) {
609              //
610              // Set candidate to override implementation to flag we found an implementation
611              //
612              overrides = true;
613              continue;
614            }
615
616            //
617            // For members with parameters we can encounter an ambiguous candidates (they match exactly)
618            // because generic type parameters could be inflated into same types
619            //
620            if (ambig_candidate == null && (entry.Kind & mkind & (MemberKind.Method | MemberKind.Indexer)) != 0) {
621              bestCandidate = null;
622              ambig_candidate = entry;
623              continue;
624            }
625
626            bestCandidate = ambig_candidate;
627            return entry;
628          }
629        }
630
631        if (container.IsInterface || ambig_candidate != null)
632          break;
633
634        container = container.BaseType;
635      } while (container != null);
636
637      return ambig_candidate;
638    }
639
640    //
641    // Returns inflated version of MemberSpec, it works similarly to
642    // SRE TypeBuilder.GetMethod
643    //
644    public static T GetMember<T> (TypeSpec container, T spec) where T : MemberSpec
645    {
646      IList<MemberSpec> applicable;
647      if (container.MemberCache.member_hash.TryGetValue (GetLookupName (spec), out applicable)) {
648        for (int i = applicable.Count - 1; i >= 0; i--) {
649          var entry = applicable[i];
650          if (entry.MemberDefinition == spec.MemberDefinition)
651            return (T) entry;
652        }
653      }
654
655      throw new InternalErrorException ("Missing member `{0}' on inflated type `{1}'",
656        spec.GetSignatureForError (), container.GetSignatureForError ());
657    }
658
659    static MemberKind GetMemberCoreKind (MemberCore member)
660    {
661      if (member is FieldBase)
662        return MemberKind.Field;
663      if (member is Indexer)
664        return MemberKind.Indexer;
665      if (member is Class)
666        return MemberKind.Class;
667      if (member is Struct)
668        return MemberKind.Struct;
669      if (member is Destructor)
670        return MemberKind.Destructor;
671      if (member is Method)
672        return MemberKind.Method;
673      if (member is Property)
674        return MemberKind.Property;
675      if (member is EventField)
676        return MemberKind.Event;
677      if (member is Interface)
678        return MemberKind.Interface;
679      if (member is EventProperty)
680        return MemberKind.Event;
681      if (member is Delegate)
682        return MemberKind.Delegate;
683      if (member is Enum)
684        return MemberKind.Enum;
685
686      throw new NotImplementedException (member.GetType ().ToString ());
687    }
688
689    public static List<FieldSpec> GetAllFieldsForDefiniteAssignment (TypeSpec container)
690    {
691      List<FieldSpec> fields = null;
692      foreach (var entry in container.MemberCache.member_hash) {
693        foreach (var name_entry in entry.Value) {
694          if (name_entry.Kind != MemberKind.Field)
695            continue;
696
697          if ((name_entry.Modifiers & Modifiers.STATIC) != 0)
698            continue;
699
700          //
701          // Fixed size buffers are not subject to definite assignment checking
702          //
703          if (name_entry is FixedFieldSpec || name_entry is ConstSpec)
704            continue;
705
706          var fs = (FieldSpec) name_entry;
707
708          //
709          // LAMESPEC: Very bizzare hack, definitive assignment is not done
710          // for imported non-public reference fields except array. No idea what the
711          // actual csc rule is
712          //
713          if (!fs.IsPublic && container.MemberDefinition.IsImported && (!fs.MemberType.IsArray && TypeSpec.IsReferenceType (fs.MemberType)))
714            continue;
715
716          //if ((fs.Modifiers & (Modifiers.BACKING_FIELD) != 0)
717          //  continue;
718
719          if (fields == null)
720            fields = new List<FieldSpec> ();
721
722          fields.Add (fs);
723          break;
724        }
725      }
726
727      return fields ?? new List<FieldSpec> (0);
728    }
729
730    public static IList<MemberSpec> GetCompletitionMembers (IMemberContext ctx, TypeSpec container, string name)
731    {
732      var matches = new List<MemberSpec> ();
733      foreach (var entry in container.MemberCache.member_hash) {
734        foreach (var name_entry in entry.Value) {
735          if (name_entry.IsAccessor)
736            continue;
737
738          if ((name_entry.Kind & (MemberKind.Constructor | MemberKind.Destructor | MemberKind.Operator)) != 0)
739            continue;
740
741          if (!name_entry.IsAccessible (ctx))
742            continue;
743
744          if (name == null || name_entry.Name.StartsWith (name)) {
745            matches.Add (name_entry);
746          }
747        }
748      }
749
750      return matches;
751    }
752
753    //
754    // Returns members of @iface only, base members are ignored
755    //
756    public static List<MethodSpec> GetInterfaceMethods (TypeSpec iface)
757    {
758      //
759      // MemberCache flatten interfaces, therefore in cases like this one
760      //
761      // interface IA : IB {}
762      // interface IB { void Foo () }
763      //
764      // we would return Foo inside IA which is not expected in this case
765      //
766      var methods = new List<MethodSpec> ();
767      foreach (var entry in iface.MemberCache.member_hash.Values) {
768        foreach (var name_entry in entry) {
769          if (iface == name_entry.DeclaringType) {
770            if (name_entry.Kind == MemberKind.Method) {
771              methods.Add ((MethodSpec) name_entry);
772            }
773          }
774        }
775      }
776
777      return methods;
778    }
779
780    //
781    // Returns all not implememted abstract members inside abstract type
782    // NOTE: Returned list is shared and must not be modified
783    //
784    public static IList<MethodSpec> GetNotImplementedAbstractMethods (TypeSpec type)
785    {
786      if (type.MemberCache.missing_abstract != null)
787        return type.MemberCache.missing_abstract;
788       
789      var abstract_methods = new List<MethodSpec> ();
790      List<TypeSpec> hierarchy = null;
791
792      //
793      // Stage 1: top-to-bottom scan for abstract members
794      //
795      var abstract_type = type;
796      while (true) {
797        foreach (var entry in abstract_type.MemberCache.member_hash) {
798          foreach (var name_entry in entry.Value) {
799            if ((name_entry.Modifiers & Modifiers.ABSTRACT) == 0)
800              continue;
801
802            var ms = name_entry as MethodSpec;
803            if (ms == null)
804              continue;
805
806            abstract_methods.Add (ms);
807          }
808        }
809
810        var base_type = abstract_type.BaseType;
811        if (!base_type.IsAbstract)
812          break;
813
814        if (hierarchy == null)
815          hierarchy = new List<TypeSpec> ();
816
817        hierarchy.Add (abstract_type);
818        abstract_type = base_type;
819      }
820
821      int not_implemented_count = abstract_methods.Count;
822      if (not_implemented_count == 0 || hierarchy == null) {
823        type.MemberCache.missing_abstract = abstract_methods;
824        return type.MemberCache.missing_abstract;
825      }
826
827      //
828      // Stage 2: Remove already implemented methods
829      //
830      foreach (var type_up in hierarchy) {
831        var members = type_up.MemberCache.member_hash;
832        if (members.Count == 0)
833          continue;
834
835        for (int i = 0; i < abstract_methods.Count; ++i) {
836          var candidate = abstract_methods [i];
837          if (candidate == null)
838            continue;
839
840          IList<MemberSpec> applicable;
841          if (!members.TryGetValue (candidate.Name, out applicable))
842            continue;
843
844          var filter = new MemberFilter (candidate);
845          foreach (var item in applicable) {
846            if ((item.Modifiers & (Modifiers.OVERRIDE | Modifiers.VIRTUAL)) == 0)
847              continue;
848
849            //
850            // Abstract override does not override anything
851            //
852            if ((item.Modifiers & Modifiers.ABSTRACT) != 0)
853              continue;
854
855            if (filter.Equals (item)) {
856              --not_implemented_count;
857              abstract_methods [i] = null;
858              break;
859            }
860          }
861        }
862      }
863
864      if (not_implemented_count == abstract_methods.Count) {
865        type.MemberCache.missing_abstract = abstract_methods;
866        return type.MemberCache.missing_abstract;
867      }
868
869      var not_implemented = new MethodSpec[not_implemented_count];
870      int counter = 0;
871      foreach (var m in abstract_methods) {
872        if (m == null)
873          continue;
874
875        not_implemented[counter++] = m;
876      }
877
878      type.MemberCache.missing_abstract = not_implemented;
879      return type.MemberCache.missing_abstract;
880    }
881
882    static string GetLookupName (MemberSpec ms)
883    {
884      if (ms.Kind == MemberKind.Indexer)
885        return IndexerNameAlias;
886
887      if (ms.Kind == MemberKind.Constructor) {
888        if (ms.IsStatic)
889          return Constructor.TypeConstructorName;
890
891        return Constructor.ConstructorName;
892      }
893
894      return ms.Name;
895    }
896
897    static string GetLookupName (MemberCore mc)
898    {
899      if (mc is Indexer)
900        return IndexerNameAlias;
901
902      if (mc is Constructor)
903        return mc.IsStatic ? Constructor.TypeConstructorName : Constructor.ConstructorName;
904
905      return mc.MemberName.Name;
906    }
907
908    //
909    // Returns all operators declared on container and its base types (until declaredOnly is used)
910    //
911    public static IList<MemberSpec> GetUserOperator (TypeSpec container, Operator.OpType op, bool declaredOnly)
912    {
913      IList<MemberSpec> found = null;
914      bool shared_list = true;
915      IList<MemberSpec> applicable;
916      do {
917        var mc = container.MemberCache;
918
919        if (((op == Operator.OpType.Implicit || op == Operator.OpType.Explicit) && (mc.state & StateFlags.HasConversionOperator) != 0) ||
920           (mc.state & StateFlags.HasUserOperator) != 0) {
921
922          if (mc.member_hash.TryGetValue (Operator.GetMetadataName (op), out applicable)) {
923            int i;
924            for (i = 0; i < applicable.Count; ++i) {
925              if (applicable[i].Kind != MemberKind.Operator) {
926                break;
927              }
928            }
929
930            //
931            // Handles very rare case where a method with same name as operator (op_xxxx) exists
932            // and we have to resize the applicable list
933            //
934            if (i != applicable.Count) {
935              for (i = 0; i < applicable.Count; ++i) {
936                if (applicable[i].Kind != MemberKind.Operator) {
937                  continue;
938                }
939
940                if (found == null) {
941                  found = new List<MemberSpec> ();
942                  found.Add (applicable[i]);
943                } else {
944                  List<MemberSpec> prev;
945                  if (shared_list) {
946                    shared_list = false;
947                    prev = new List<MemberSpec> (found.Count + 1);
948                    prev.AddRange (found);
949                  } else {
950                    prev = (List<MemberSpec>) found;
951                  }
952
953                  prev.Add (applicable[i]);
954                }
955              }
956            } else {
957              if (found == null) {
958                found = applicable;
959                shared_list = true;
960              } else {
961                List<MemberSpec> merged;
962                if (shared_list) {
963                  shared_list = false;
964                  merged = new List<MemberSpec> (found.Count + applicable.Count);
965                  merged.AddRange (found);
966                  found = merged;
967                } else {
968                  merged = (List<MemberSpec>) found;
969                }
970
971                merged.AddRange (applicable);
972              }
973            }
974          }
975        }
976
977        // BaseType call can be expensive
978        if (declaredOnly)
979          break;
980
981        container = container.BaseType;
982      } while (container != null);
983
984      return found;
985    }
986
987    //
988    // Inflates all member cache nested types
989    //
990    public void InflateTypes (MemberCache inflated_cache, TypeParameterInflator inflator)
991    {
992      foreach (var item in member_hash) {
993        IList<MemberSpec> inflated_members = null;
994        for (int i = 0; i < item.Value.Count; ++i ) {
995          var member = item.Value[i];
996
997          // FIXME: When inflating members refering nested types before they are inflated
998          if (member == null)
999            continue;
1000
1001          if ((member.Kind & MemberKind.NestedMask) != 0 &&
1002            (member.Modifiers & Modifiers.COMPILER_GENERATED) == 0) {
1003            if (inflated_members == null) {
1004              inflated_members = new MemberSpec[item.Value.Count];
1005              inflated_cache.member_hash.Add (item.Key, inflated_members);
1006            }
1007
1008            inflated_members [i] = member.InflateMember (inflator);
1009          }
1010        }
1011      }
1012    }
1013
1014    //
1015    // Inflates all open type members, requires InflateTypes to be called before
1016    //
1017    public void InflateMembers (MemberCache cacheToInflate, TypeSpec inflatedType, TypeParameterInflator inflator)
1018    {
1019      var inflated_member_hash = cacheToInflate.member_hash;
1020      Dictionary<MemberSpec, MethodSpec> accessor_relation = null;
1021      List<MemberSpec> accessor_members = null;
1022
1023      // Copy member specific flags when all members were added
1024      cacheToInflate.state = state;
1025
1026      foreach (var item in member_hash) {
1027        var members = item.Value;
1028        IList<MemberSpec> inflated_members = null;
1029        for (int i = 0; i < members.Count; ++i ) {
1030          var member = members[i];
1031
1032          //
1033          // All nested types have been inflated earlier except for
1034          // compiler types which are created later and could miss InflateTypes
1035          //
1036          if ((member.Kind & MemberKind.NestedMask) != 0 &&
1037            (member.Modifiers & Modifiers.COMPILER_GENERATED) == 0) {
1038            if (inflated_members == null)
1039              inflated_members = inflated_member_hash[item.Key];
1040
1041            continue;
1042          }
1043
1044          //
1045          // Clone the container first
1046          //
1047          if (inflated_members == null) {
1048            inflated_members = new MemberSpec [item.Value.Count];
1049            inflated_member_hash.Add (item.Key, inflated_members);
1050          }
1051
1052          var local_inflator = inflator;
1053
1054          if (member.DeclaringType != inflatedType) {
1055            //
1056            // Don't inflate top-level non-generic interface members
1057            // merged into generic interface
1058            //
1059            if (!member.DeclaringType.IsGeneric && !member.DeclaringType.IsNested) {
1060              inflated_members [i] = member;
1061              continue;
1062            }
1063
1064            //
1065            // Needed when inflating flatten interfaces. It inflates
1066            // container type only, type parameters are already done
1067            //
1068            // Handles cases like:
1069            //
1070            // interface I<T> {}
1071            // interface I<U, V> : I<U> {}
1072            //
1073            // class C: I<int, bool> {}
1074            //
1075            var inflated_parent = inflator.Inflate (member.DeclaringType);
1076            if (inflated_parent != inflator.TypeInstance)
1077              local_inflator = new TypeParameterInflator (inflator, inflated_parent);
1078          }
1079
1080          //
1081          // Inflate every member, its parent is now different
1082          //
1083          var inflated = member.InflateMember (local_inflator);
1084          inflated_members [i] = inflated;
1085
1086          if (member is PropertySpec || member is EventSpec) {
1087            if (accessor_members == null)
1088              accessor_members = new List<MemberSpec> ();
1089
1090            accessor_members.Add (inflated);
1091            continue;
1092          }
1093
1094          if (member.IsAccessor) {
1095            if (accessor_relation == null)
1096              accessor_relation = new Dictionary<MemberSpec, MethodSpec> ();
1097            accessor_relation.Add (member, (MethodSpec) inflated);
1098          }
1099        }
1100      }
1101
1102      if (accessor_members != null) {
1103        foreach (var member in accessor_members) {
1104          var prop = member as PropertySpec;
1105          if (prop != null) {
1106            if (prop.Get != null)
1107              prop.Get = accessor_relation[prop.Get];
1108            if (prop.Set != null)
1109              prop.Set = accessor_relation[prop.Set];
1110
1111            continue;
1112          }
1113
1114          var ev = (EventSpec) member;
1115          ev.AccessorAdd = accessor_relation[ev.AccessorAdd];
1116          ev.AccessorRemove = accessor_relation[ev.AccessorRemove];
1117        }
1118      }
1119    }
1120
1121    //
1122    // Removes hidden base members of an interface. For compiled interfaces we cannot
1123    // do name filtering during Add (as we do for import) because we need all base
1124    // names to be valid during type definition.
1125    // Add replaces hidden base member with current one which means any name collision
1126    // (CS0108) of non-first name would be unnoticed because the name was replaced
1127    // with the one from compiled type
1128    //
1129    public void RemoveHiddenMembers (TypeSpec container)
1130    {
1131      foreach (var entry in member_hash) {
1132        var values = entry.Value;
1133
1134        int container_members_start_at = 0;
1135        while (values[container_members_start_at].DeclaringType != container && ++container_members_start_at < entry.Value.Count);
1136
1137        if (container_members_start_at == 0 || container_members_start_at == values.Count)
1138          continue;
1139
1140        for (int i = 0; i < container_members_start_at; ++i) {
1141          var member = values[i];
1142
1143          if (!container.ImplementsInterface (member.DeclaringType, false))
1144            continue;
1145
1146          var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : ParametersCompiled.EmptyReadOnlyParameters;
1147
1148          for (int ii = container_members_start_at; ii < values.Count; ++ii) {
1149            var container_entry = values[ii];
1150
1151            if (container_entry.Arity != member.Arity)
1152              continue;
1153
1154            if (container_entry is IParametersMember) {
1155              if (!TypeSpecComparer.Override.IsEqual (((IParametersMember) container_entry).Parameters, member_param))
1156                continue;
1157            }
1158
1159            values.RemoveAt (i);
1160            --container_members_start_at;
1161            --ii;
1162            --i;
1163          }
1164        }
1165      }
1166    }
1167
1168    //
1169    // Checks all appropriate container members for CLS compliance
1170    //
1171    public void VerifyClsCompliance (TypeSpec container, Report report)
1172    {
1173      if (locase_members != null)
1174        return;
1175
1176      if (container.BaseType == null) {
1177        locase_members = new Dictionary<string, MemberSpec[]> (member_hash.Count); // StringComparer.OrdinalIgnoreCase);
1178      } else {
1179        var btype = container.BaseType.GetDefinition ();
1180        btype.MemberCache.VerifyClsCompliance (btype, report);
1181        locase_members = new Dictionary<string, MemberSpec[]> (btype.MemberCache.locase_members); //, StringComparer.OrdinalIgnoreCase);
1182      }
1183
1184      var is_imported_type = container.MemberDefinition.IsImported;
1185      foreach (var entry in container.MemberCache.member_hash) {
1186        for (int i = 0; i < entry.Value.Count; ++i ) {
1187          var name_entry = entry.Value[i];
1188          if ((name_entry.Modifiers & (Modifiers.PUBLIC | Modifiers.PROTECTED)) == 0)
1189            continue;
1190
1191          if ((name_entry.Modifiers & (Modifiers.OVERRIDE | Modifiers.COMPILER_GENERATED)) != 0)
1192            continue;
1193
1194          if ((name_entry.Kind & MemberKind.MaskType) == 0)
1195            continue;
1196
1197          if (name_entry.MemberDefinition.CLSAttributeValue == false)
1198              continue;
1199
1200          IParametersMember p_a = null;
1201          if (!is_imported_type) {
1202            p_a = name_entry as IParametersMember;
1203            if (p_a != null && !name_entry.IsAccessor) {
1204              var p_a_pd = p_a.Parameters;
1205              //
1206              // Check differing overloads in @container
1207              //
1208              for (int ii = i + 1; ii < entry.Value.Count; ++ii) {
1209                var checked_entry = entry.Value[ii];
1210                IParametersMember p_b = checked_entry as IParametersMember;
1211                if (p_b == null)
1212                  continue;
1213
1214                if (p_a_pd.Count != p_b.Parameters.Count)
1215                  continue;
1216
1217                if (checked_entry.IsAccessor)
1218                  continue;
1219
1220                var res = ParametersCompiled.IsSameClsSignature (p_a.Parameters, p_b.Parameters);
1221                if (res != 0) {
1222                  ReportOverloadedMethodClsDifference (name_entry, checked_entry, res, report);
1223                }
1224              }
1225            }
1226          }
1227
1228          if (i > 0 || name_entry.Kind == MemberKind.Constructor || name_entry.Kind == MemberKind.Indexer)
1229            continue;
1230
1231          var name_entry_locase = name_entry.Name.ToLowerInvariant ();
1232
1233          MemberSpec[] found;
1234          if (!locase_members.TryGetValue (name_entry_locase, out found)) {
1235            found = new MemberSpec[] { name_entry };
1236            locase_members.Add (name_entry_locase, found);
1237          } else {
1238            bool same_names_only = true;
1239            foreach (var f in found) {
1240              if (f.Name == name_entry.Name) {
1241                if (p_a != null) {
1242                  IParametersMember p_b = f as IParametersMember;
1243                  if (p_b == null)
1244                    continue;
1245
1246                  if (p_a.Parameters.Count != p_b.Parameters.Count)
1247                    continue;
1248
1249                  if (f.IsAccessor)
1250                    continue;
1251
1252                  var res = ParametersCompiled.IsSameClsSignature (p_a.Parameters, p_b.Parameters);
1253                  if (res != 0) {
1254                    ReportOverloadedMethodClsDifference (f, name_entry, res, report);
1255                  }
1256                }
1257
1258                continue;
1259              }
1260
1261              same_names_only = false;
1262              if (!is_imported_type) {
1263                var last = GetLaterDefinedMember (f, name_entry);
1264                if (last == f.MemberDefinition) {
1265                  report.SymbolRelatedToPreviousError (name_entry);
1266                } else {
1267                  report.SymbolRelatedToPreviousError (f);
1268                }
1269
1270                report.Warning (3005, 1, last.Location,
1271                  "Identifier `{0}' differing only in case is not CLS-compliant", last.GetSignatureForError ());
1272              }
1273            }
1274
1275            if (!same_names_only) {
1276              Array.Resize (ref found, found.Length + 1);
1277              found[found.Length - 1] = name_entry;
1278              locase_members[name_entry_locase] = found;
1279            }
1280          }
1281        }
1282      }
1283    }
1284
1285    //
1286    // Local report helper to issue correctly ordered members stored in hashtable
1287    //
1288    static MemberCore GetLaterDefinedMember (MemberSpec a, MemberSpec b)
1289    {
1290      var mc_a = a.MemberDefinition as MemberCore;
1291      var mc_b = b.MemberDefinition as MemberCore;
1292      if (mc_a == null)
1293        return mc_b;
1294
1295      if (mc_b == null)
1296        return mc_a;
1297
1298      if (a.DeclaringType.MemberDefinition != b.DeclaringType.MemberDefinition)
1299        return mc_b;
1300
1301      if (mc_a.Location.File != mc_a.Location.File)
1302        return mc_b;
1303
1304      return mc_b.Location.Row > mc_a.Location.Row ? mc_b : mc_a;
1305    }
1306
1307    static void ReportOverloadedMethodClsDifference (MemberSpec a, MemberSpec b, int res, Report report)
1308    {
1309      var last = GetLaterDefinedMember (a, b);
1310      if (last == a.MemberDefinition) {
1311        report.SymbolRelatedToPreviousError (b);
1312      } else {
1313        report.SymbolRelatedToPreviousError (a);
1314      }
1315
1316      if ((res & 1) != 0) {
1317        report.Warning (3006, 1, last.Location,
1318            "Overloaded method `{0}' differing only in ref or out, or in array rank, is not CLS-compliant",
1319            last.GetSignatureForError ());
1320      }
1321
1322      if ((res & 2) != 0) {
1323        report.Warning (3007, 1, last.Location,
1324          "Overloaded method `{0}' differing only by unnamed array types is not CLS-compliant",
1325          last.GetSignatureForError ());
1326      }
1327    }
1328
1329    public bool CheckExistingMembersOverloads (MemberCore member, AParametersCollection parameters)
1330    {
1331      var name = GetLookupName (member);
1332      var imb = member as InterfaceMemberBase;
1333      if (imb != null && imb.IsExplicitImpl) {
1334        name = imb.GetFullName (name);
1335      }
1336
1337      return CheckExistingMembersOverloads (member, name, parameters);
1338    }
1339
1340    public bool CheckExistingMembersOverloads (MemberCore member, string name, AParametersCollection parameters)
1341    {
1342      IList<MemberSpec> entries;
1343      if (!member_hash.TryGetValue (name, out entries))
1344        return false;
1345
1346      var Report = member.Compiler.Report;
1347
1348      int method_param_count = parameters.Count;
1349      for (int i = entries.Count - 1; i >= 0; --i) {
1350        var ce = entries[i];
1351        var pm = ce as IParametersMember;
1352        var pd = pm == null ? ParametersCompiled.EmptyReadOnlyParameters : pm.Parameters;
1353        if (pd.Count != method_param_count)
1354          continue;
1355
1356        if (ce.Arity != member.MemberName.Arity)
1357          continue;
1358
1359        // Ignore merged interface members
1360        if (member.Parent.PartialContainer != ce.DeclaringType.MemberDefinition)
1361          continue;
1362
1363        var p_types = pd.Types;
1364        if (method_param_count > 0) {
1365          int ii = method_param_count - 1;
1366          TypeSpec type_a, type_b;
1367          do {
1368            type_a = parameters.Types [ii];
1369            type_b = p_types [ii];
1370
1371            var a_byref = (pd.FixedParameters[ii].ModFlags & Parameter.Modifier.RefOutMask) != 0;
1372            var b_byref = (parameters.FixedParameters[ii].ModFlags & Parameter.Modifier.RefOutMask) != 0;
1373
1374            if (a_byref != b_byref)
1375              break;
1376
1377          } while (TypeSpecComparer.Override.IsEqual (type_a, type_b) && ii-- != 0);
1378
1379          if (ii >= 0)
1380            continue;
1381
1382          //
1383          // Operators can differ in return type only
1384          //
1385          if (member is Operator && ce.Kind == MemberKind.Operator && ((MethodSpec) ce).ReturnType != ((Operator) member).ReturnType)
1386            continue;
1387
1388          //
1389          // Report difference in parameter modifiers only
1390          //
1391          if (pd != null && member is MethodCore) {
1392            ii = method_param_count;
1393            while (ii-- != 0 &&
1394              (parameters.FixedParameters[ii].ModFlags & Parameter.Modifier.ModifierMask) ==
1395              (pd.FixedParameters[ii].ModFlags & Parameter.Modifier.ModifierMask) &&
1396              parameters.ExtensionMethodType == pd.ExtensionMethodType) ;
1397
1398            if (ii >= 0) {
1399              var mc = ce as MethodSpec;
1400              member.Compiler.Report.SymbolRelatedToPreviousError (ce);
1401              if ((member.ModFlags & Modifiers.PARTIAL) != 0 && (mc.Modifiers & Modifiers.PARTIAL) != 0) {
1402                if (parameters.HasParams || pd.HasParams) {
1403                  Report.Error (758, member.Location,
1404                    "A partial method declaration and partial method implementation cannot differ on use of `params' modifier");
1405                } else {
1406                  Report.Error (755, member.Location,
1407                    "A partial method declaration and partial method implementation must be both an extension method or neither");
1408                }
1409              } else if (member is Constructor) {
1410                Report.Error (851, member.Location,
1411                  "Overloaded contructor `{0}' cannot differ on use of parameter modifiers only",
1412                  member.GetSignatureForError ());
1413              } else {
1414                Report.Error (663, member.Location,
1415                  "Overloaded method `{0}' cannot differ on use of parameter modifiers only",
1416                  member.GetSignatureForError ());
1417              }
1418              return false;
1419            }
1420          }
1421        }
1422
1423        if ((ce.Kind & MemberKind.Method) != 0) {
1424          Method method_a = member as Method;
1425          Method method_b = ce.MemberDefinition as Method;
1426          if (method_a != null && method_b != null && (method_a.ModFlags & method_b.ModFlags & Modifiers.PARTIAL) != 0) {
1427            const Modifiers partial_modifiers = Modifiers.STATIC | Modifiers.UNSAFE;
1428            if (method_a.IsPartialDefinition == method_b.IsPartialImplementation) {
1429              if ((method_a.ModFlags & partial_modifiers) == (method_b.ModFlags & partial_modifiers) ||
1430                method_a.Parent.IsUnsafe && method_b.Parent.IsUnsafe) {
1431                if (method_a.IsPartialImplementation) {
1432                  method_a.SetPartialDefinition (method_b);
1433                  if (entries.Count == 1)
1434                    member_hash.Remove (name);
1435                  else
1436                    entries.RemoveAt (i);
1437                } else {
1438                  method_b.SetPartialDefinition (method_a);
1439                  method_a.caching_flags |= MemberCore.Flags.PartialDefinitionExists;
1440                }
1441                continue;
1442              }
1443
1444              if (method_a.IsStatic != method_b.IsStatic) {
1445                Report.SymbolRelatedToPreviousError (ce);
1446                Report.Error (763, member.Location,
1447                  "A partial method declaration and partial method implementation must be both `static' or neither");
1448              }
1449
1450              if ((method_a.ModFlags & Modifiers.UNSAFE) != (method_b.ModFlags & Modifiers.UNSAFE)) {
1451                Report.SymbolRelatedToPreviousError (ce);
1452                Report.Error (764, member.Location,
1453                  "A partial method declaration and partial method implementation must be both `unsafe' or neither");
1454              }
1455
1456              return false;
1457            }
1458
1459            Report.SymbolRelatedToPreviousError (ce);
1460            if (method_a.IsPartialDefinition) {
1461              Report.Error (756, member.Location, "A partial method `{0}' declaration is already defined",
1462                member.GetSignatureForError ());
1463            }
1464
1465            Report.Error (757, member.Location, "A partial method `{0}' implementation is already defined",
1466              member.GetSignatureForError ());
1467            return false;
1468          }
1469
1470          Report.SymbolRelatedToPreviousError (ce);
1471
1472          bool is_reserved_a = member is AbstractPropertyEventMethod || member is Operator;
1473          bool is_reserved_b = ((MethodSpec) ce).IsReservedMethod;
1474
1475          if (is_reserved_a || is_reserved_b) {
1476            Report.Error (82, member.Location, "A member `{0}' is already reserved",
1477              is_reserved_a ?
1478              ce.GetSignatureForError () :
1479              member.GetSignatureForError ());
1480            return false;
1481          }
1482        } else {
1483          Report.SymbolRelatedToPreviousError (ce);
1484        }
1485
1486        if (member is Operator && ce.Kind == MemberKind.Operator) {
1487          Report.Error (557, member.Location, "Duplicate user-defined conversion in type `{0}'",
1488            member.Parent.GetSignatureForError ());
1489          return false;
1490        }
1491
1492        Report.Error (111, member.Location,
1493          "A member `{0}' is already defined. Rename this member or use different parameter types",
1494          member.GetSignatureForError ());
1495        return false;
1496      }
1497
1498      return true;
1499    }
1500  }
1501}
Note: See TracBrowser for help on using the repository browser.