Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Parser/mcs/pending.cs @ 15888

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

#2077: created branch and added first version

File size: 21.8 KB
Line 
1//
2// pending.cs: Pending method implementation
3//
4// Authors:
5//   Miguel de Icaza (miguel@gnu.org)
6//   Marek Safar (marek.safar@gmail.com)
7//
8// Dual licensed under the terms of the MIT X11 or GNU GPL
9//
10// Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
11// Copyright 2003-2008 Novell, Inc.
12// Copyright 2011 Xamarin Inc
13//
14
15using System;
16using System.Collections.Generic;
17using System.Linq;
18
19#if STATIC
20using IKVM.Reflection;
21using IKVM.Reflection.Emit;
22#else
23using System.Reflection;
24using System.Reflection.Emit;
25#endif
26
27namespace Mono.CSharp {
28
29  struct TypeAndMethods {
30    public TypeSpec          type;
31    public IList<MethodSpec> methods;
32
33    //
34    // Whether it is optional, this is used to allow the explicit/implicit
35    // implementation when a base class already implements an interface.
36    //
37    // For example:
38    //
39    // class X : IA { }  class Y : X, IA { IA.Explicit (); }
40    //
41    public bool          optional;
42       
43    //
44    // This flag on the method says `We found a match, but
45    // because it was private, we could not use the match
46    //
47    public MethodData [] found;
48
49    // If a method is defined here, then we always need to
50    // create a proxy for it.  This is used when implementing
51    // an interface's indexer with a different IndexerName.
52    public MethodSpec [] need_proxy;
53  }
54
55  struct ProxyMethodContext : IMemberContext
56  {
57    readonly TypeContainer container;
58
59    public ProxyMethodContext (TypeContainer container)
60    {
61      this.container = container;
62    }
63
64    public TypeSpec CurrentType {
65      get {
66        throw new NotImplementedException ();
67      }
68    }
69
70    public TypeParameters CurrentTypeParameters {
71      get {
72        throw new NotImplementedException ();
73      }
74    }
75
76    public MemberCore CurrentMemberDefinition {
77      get {
78        throw new NotImplementedException ();
79      }
80    }
81
82    public bool IsObsolete {
83      get {
84        return false;
85      }
86    }
87
88    public bool IsUnsafe {
89      get {
90        throw new NotImplementedException ();
91      }
92    }
93
94    public bool IsStatic {
95      get {
96        return false;
97      }
98    }
99
100    public ModuleContainer Module {
101      get {
102        return container.Module;
103      }
104    }
105
106    public string GetSignatureForError ()
107    {
108      throw new NotImplementedException ();
109    }
110
111    public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity)
112    {
113      throw new NotImplementedException ();
114    }
115
116    public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
117    {
118      throw new NotImplementedException ();
119    }
120
121    public FullNamedExpression LookupNamespaceAlias (string name)
122    {
123      throw new NotImplementedException ();
124    }
125  }
126
127  public class PendingImplementation
128  {
129    /// <summary>
130    ///   The container for this PendingImplementation
131    /// </summary>
132    readonly TypeDefinition container;
133   
134    /// <summary>
135    ///   This is the array of TypeAndMethods that describes the pending implementations
136    ///   (both interfaces and abstract methods in base class)
137    /// </summary>
138    TypeAndMethods [] pending_implementations;
139
140    PendingImplementation (TypeDefinition container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total)
141    {
142      var type_builder = container.Definition;
143     
144      this.container = container;
145      pending_implementations = new TypeAndMethods [total];
146
147      int i = 0;
148      if (abstract_methods != null) {
149        int count = abstract_methods.Length;
150        pending_implementations [i].methods = new MethodSpec [count];
151        pending_implementations [i].need_proxy = new MethodSpec [count];
152
153        pending_implementations [i].methods = abstract_methods;
154        pending_implementations [i].found = new MethodData [count];
155        pending_implementations [i].type = type_builder;
156        ++i;
157      }
158
159      foreach (MissingInterfacesInfo missing in missing_ifaces) {
160        var iface = missing.Type;
161        var mi = MemberCache.GetInterfaceMethods (iface);
162
163        int count = mi.Count;
164        pending_implementations [i].type = iface;
165        pending_implementations [i].optional = missing.Optional;
166        pending_implementations [i].methods = mi;
167        pending_implementations [i].found = new MethodData [count];
168        pending_implementations [i].need_proxy = new MethodSpec [count];
169        i++;
170      }
171    }
172
173    Report Report {
174      get {
175        return container.Module.Compiler.Report;
176      }
177    }
178
179    struct MissingInterfacesInfo {
180      public TypeSpec Type;
181      public bool Optional;
182
183      public MissingInterfacesInfo (TypeSpec t)
184      {
185        Type = t;
186        Optional = false;
187      }
188    }
189
190    static readonly MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
191   
192    static MissingInterfacesInfo [] GetMissingInterfaces (TypeDefinition container)
193    {
194      //
195      // Interfaces will return all interfaces that the container
196      // implements including any inherited interfaces
197      //
198      var impl = container.Definition.Interfaces;
199
200      if (impl == null || impl.Count == 0)
201        return EmptyMissingInterfacesInfo;
202
203      var ret = new MissingInterfacesInfo[impl.Count];
204
205      for (int i = 0; i < ret.Length; i++)
206        ret [i] = new MissingInterfacesInfo (impl [i]);
207
208      // we really should not get here because Object doesnt implement any
209      // interfaces. But it could implement something internal, so we have
210      // to handle that case.
211      if (container.BaseType == null)
212        return ret;
213     
214      var base_impls = container.BaseType.Interfaces;
215      if (base_impls != null) {
216        foreach (TypeSpec t in base_impls) {
217          for (int i = 0; i < ret.Length; i++) {
218            if (t == ret[i].Type) {
219              ret[i].Optional = true;
220              break;
221            }
222          }
223        }
224      }
225
226      return ret;
227    }
228   
229    //
230    // Factory method: if there are pending implementation methods, we return a PendingImplementation
231    // object, otherwise we return null.
232    //
233    // Register method implementations are either abstract methods
234    // flagged as such on the base class or interface methods
235    //
236    static public PendingImplementation GetPendingImplementations (TypeDefinition container)
237    {
238      TypeSpec b = container.BaseType;
239
240      var missing_interfaces = GetMissingInterfaces (container);
241
242      //
243      // If we are implementing an abstract class, and we are not
244      // ourselves abstract, and there are abstract methods (C# allows
245      // abstract classes that have no abstract methods), then allocate
246      // one slot.
247      //
248      // We also pre-compute the methods.
249      //
250      bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
251      MethodSpec[] abstract_methods = null;
252
253      if (implementing_abstract){
254        var am = MemberCache.GetNotImplementedAbstractMethods (b);
255
256        if (am == null) {
257          implementing_abstract = false;
258        } else {
259          abstract_methods = new MethodSpec[am.Count];
260          am.CopyTo (abstract_methods, 0);
261        }
262      }
263     
264      int total = missing_interfaces.Length +  (implementing_abstract ? 1 : 0);
265      if (total == 0)
266        return null;
267
268      var pending = new PendingImplementation (container, missing_interfaces, abstract_methods, total);
269
270      //
271      // check for inherited conflicting methods
272      //
273      foreach (var p in pending.pending_implementations) {
274        //
275        // It can happen for generic interfaces only
276        //
277        if (!p.type.IsGeneric)
278          continue;
279
280        //
281        // CLR does not distinguishes between ref and out
282        //
283        for (int i = 0; i < p.methods.Count; ++i) {
284          MethodSpec compared_method = p.methods[i];
285          if (compared_method.Parameters.IsEmpty)
286            continue;
287
288          for (int ii = i + 1; ii < p.methods.Count; ++ii) {
289            MethodSpec tested_method = p.methods[ii];
290            if (compared_method.Name != tested_method.Name)
291              continue;
292
293            if (p.type != tested_method.DeclaringType)
294              continue;
295
296            if (!TypeSpecComparer.Override.IsSame (compared_method.Parameters.Types, tested_method.Parameters.Types))
297              continue;
298
299            bool exact_match = true;
300            bool ref_only_difference = false;
301            var cp = compared_method.Parameters.FixedParameters;
302            var tp = tested_method.Parameters.FixedParameters;
303
304            for (int pi = 0; pi < cp.Length; ++pi) {
305              //
306              // First check exact modifiers match
307              //
308              if ((cp[pi].ModFlags & Parameter.Modifier.RefOutMask) == (tp[pi].ModFlags & Parameter.Modifier.RefOutMask))
309                continue;
310
311              if (((cp[pi].ModFlags | tp[pi].ModFlags) & Parameter.Modifier.RefOutMask) == Parameter.Modifier.RefOutMask) {
312                ref_only_difference = true;
313                continue;
314              }
315
316              exact_match = false;
317              break;
318            }
319
320            if (!exact_match || !ref_only_difference)
321              continue;
322
323            pending.Report.SymbolRelatedToPreviousError (compared_method);
324            pending.Report.SymbolRelatedToPreviousError (tested_method);
325            pending.Report.Error (767, container.Location,
326              "Cannot implement interface `{0}' with the specified type parameters because it causes method `{1}' to differ on parameter modifiers only",
327              p.type.GetDefinition().GetSignatureForError (), compared_method.GetSignatureForError ());
328
329            break;
330          }
331        }
332      }
333
334      return pending;
335    }
336
337    public enum Operation {
338      //
339      // If you change this, review the whole InterfaceMethod routine as there
340      // are a couple of assumptions on these three states
341      //
342      Lookup, ClearOne, ClearAll
343    }
344
345    /// <summary>
346    ///   Whether the specified method is an interface method implementation
347    /// </summary>
348    public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate, ref bool optional)
349    {
350      return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate, ref optional);
351    }
352
353    public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate, ref bool optional)
354    {
355      InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate, ref optional);
356    }
357
358    /// <remarks>
359    ///   If a method in Type `t' (or null to look in all interfaces
360    ///   and the base abstract class) with name `Name', return type `ret_type' and
361    ///   arguments `args' implements an interface, this method will
362    ///   return the MethodInfo that this method implements.
363    ///
364    ///   If `name' is null, we operate solely on the method's signature.  This is for
365    ///   instance used when implementing indexers.
366    ///
367    ///   The `Operation op' controls whether to lookup, clear the pending bit, or clear
368    ///   all the methods with the given signature.
369    ///
370    ///   The `MethodInfo need_proxy' is used when we're implementing an interface's
371    ///   indexer in a class.  If the new indexer's IndexerName does not match the one
372    ///   that was used in the interface, then we always need to create a proxy for it.
373    ///
374    /// </remarks>
375    public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate, ref bool optional)
376    {
377      ambiguousCandidate = null;
378
379      if (pending_implementations == null)
380        return null;
381
382      TypeSpec ret_type = method.method.ReturnType;
383      ParametersCompiled args = method.method.ParameterInfo;
384      bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod;
385      MethodSpec m;
386
387      foreach (TypeAndMethods tm in pending_implementations){
388        if (!(iType == null || tm.type == iType))
389          continue;
390
391        int method_count = tm.methods.Count;
392        for (int i = 0; i < method_count; i++){
393          m = tm.methods [i];
394
395          if (m == null)
396            continue;
397
398          if (is_indexer) {
399            if (!m.IsAccessor || m.Parameters.IsEmpty)
400              continue;
401          } else {
402            if (name.Name != m.Name)
403              continue;
404
405            if (m.Arity != name.Arity)
406              continue;
407          }
408
409          if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
410            continue;
411
412          if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
413            tm.found[i] = method;
414            continue;
415          }
416
417          //
418          // `need_proxy' is not null when we're implementing an
419          // interface indexer and this is Clear(One/All) operation.
420          //
421          // If `name' is null, then we do a match solely based on the
422          // signature and not on the name (this is done in the Lookup
423          // for an interface indexer).
424          //
425          if (op != Operation.Lookup) {
426            if (m.IsAccessor != method.method.IsAccessor)
427              continue;
428
429            // If `t != null', then this is an explicitly interface
430            // implementation and we can always clear the method.
431            // `need_proxy' is not null if we're implementing an
432            // interface indexer.  In this case, we need to create
433            // a proxy if the implementation's IndexerName doesn't
434            // match the IndexerName in the interface.
435            if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) {  // TODO: This is very expensive comparison
436              tm.need_proxy[i] = method.method.Spec;
437            } else {
438              tm.methods[i] = null;
439            }
440          } else {
441            tm.found [i] = method;
442            optional = tm.optional;
443          }
444
445          if (op == Operation.Lookup && name.ExplicitInterface != null && ambiguousCandidate == null) {
446            ambiguousCandidate = m;
447            continue;
448          }
449
450          //
451          // Lookups and ClearOne return
452          //
453          if (op != Operation.ClearAll)
454            return m;
455        }
456
457        // If a specific type was requested, we can stop now.
458        if (tm.type == iType)
459          break;
460      }
461
462      m = ambiguousCandidate;
463      ambiguousCandidate = null;
464      return m;
465    }
466
467    /// <summary>
468    ///   C# allows this kind of scenarios:
469    ///   interface I { void M (); }
470    ///   class X { public void M (); }
471    ///   class Y : X, I { }
472    ///
473    ///   For that case, we create an explicit implementation function
474    ///   I.M in Y.
475    /// </summary>
476    void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
477    {
478      // TODO: Handle nested iface names
479      string proxy_name;
480      var ns = iface.MemberDefinition.Namespace;
481      if (string.IsNullOrEmpty (ns))
482        proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
483      else
484        proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
485
486      var param = iface_method.Parameters;
487
488      MethodBuilder proxy = container.TypeBuilder.DefineMethod (
489        proxy_name,
490        MethodAttributes.Private |
491        MethodAttributes.HideBySig |
492        MethodAttributes.NewSlot |
493        MethodAttributes.CheckAccessOnOverride |
494        MethodAttributes.Virtual | MethodAttributes.Final,
495        CallingConventions.Standard | CallingConventions.HasThis,
496        base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
497
498      if (iface_method.IsGeneric) {
499        var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
500        proxy.DefineGenericParameters (gnames);
501      }
502
503      for (int i = 0; i < param.Count; i++) {
504        string name = param.FixedParameters [i].Name;
505        ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags);
506        proxy.DefineParameter (i + 1, attr, name);
507      }
508
509      int top = param.Count;
510      var ec = new EmitContext (new ProxyMethodContext (container), proxy.GetILGenerator (), null, null);
511      ec.EmitThis ();
512      // TODO: GetAllParametersArguments
513      for (int i = 0; i < top; i++)
514        ec.EmitArgumentLoad (i);
515
516      ec.Emit (OpCodes.Call, base_method);
517      ec.Emit (OpCodes.Ret);
518
519      container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
520    }
521   
522    /// <summary>
523    ///   This function tells whether one of our base classes implements
524    ///   the given method (which turns out, it is valid to have an interface
525    ///   implementation in a base
526    /// </summary>
527    bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
528    {
529      base_method = null;
530      var base_type = container.BaseType;
531
532      //
533      // Setup filter with no return type to give better error message
534      // about mismatch at return type when the check bellow rejects them
535      //
536      var parameters = mi.Parameters;
537      MethodSpec close_match = null;
538
539      while (true) {
540        var candidates = MemberCache.FindMembers (base_type, mi.Name, false);
541        if (candidates == null) {
542          base_method = close_match;
543          return false;
544        }
545
546        MethodSpec similar_candidate = null;
547        foreach (var candidate in candidates) {
548          if (candidate.Kind != MemberKind.Method)
549            continue;
550
551          if (candidate.Arity != mi.Arity)
552            continue;
553
554          var candidate_param = ((MethodSpec) candidate).Parameters;
555          if (!TypeSpecComparer.Override.IsEqual (parameters.Types, candidate_param.Types))
556            continue;
557
558          bool modifiers_match = true;
559          for (int i = 0; i < parameters.Count; ++i) {
560            //
561            // First check exact ref/out match
562            //
563            if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) == (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask))
564              continue;
565
566            modifiers_match = false;
567
568            //
569            // Different in ref/out only
570            //
571            if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) {
572              if (similar_candidate == null) {
573                if (!candidate.IsPublic)
574                  break;
575
576                if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, ((MethodSpec) candidate).ReturnType))
577                  break;
578
579                // It's used for ref/out ambiguity overload check
580                similar_candidate = (MethodSpec) candidate;
581              }
582
583              continue;
584            }
585
586            similar_candidate = null;
587            break;
588          }
589
590          if (!modifiers_match)
591            continue;
592
593          //
594          // From this point the candidate is used for detailed error reporting
595          // because it's very close match to what we are looking for
596          //
597          var m = (MethodSpec) candidate;
598
599          if (!m.IsPublic) {
600            if (close_match == null)
601              close_match = m;
602
603            continue;
604          }
605
606          if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, m.ReturnType)) {
607            if (close_match == null)
608              close_match = m;
609
610            continue;
611          }
612           
613          base_method = m;
614
615          if (mi.IsGeneric && !Method.CheckImplementingMethodConstraints (container, m, mi)) {
616            return true;
617          }
618        }
619       
620        if (base_method != null) {
621          if (similar_candidate != null) {
622            Report.SymbolRelatedToPreviousError (similar_candidate);
623            Report.SymbolRelatedToPreviousError (mi);
624            Report.SymbolRelatedToPreviousError (container);
625            Report.Warning (1956, 1, ((MemberCore) base_method.MemberDefinition).Location,
626              "The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'",
627              mi.GetSignatureForError (), base_method.GetSignatureForError (), similar_candidate.GetSignatureForError (), container.GetSignatureForError ());
628          }
629
630          break;
631        }
632
633        base_type = candidates[0].DeclaringType.BaseType;
634        if (base_type == null) {
635          base_method = close_match;
636          return false;
637        }
638      }
639
640      if (!base_method.IsVirtual) {
641#if STATIC
642        var base_builder = base_method.GetMetaInfo () as MethodBuilder;
643        if (base_builder != null) {
644          //
645          // We can avoid creating a proxy if base_method can be marked 'final virtual'. This can
646          // be done for all methods from compiled assembly
647          //
648          base_builder.__SetAttributes (base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot);
649          return true;
650        }
651#endif
652        DefineProxy (iface_type, base_method, mi);
653      }
654
655      return true;
656    }
657
658    /// <summary>
659    ///   Verifies that any pending abstract methods or interface methods
660    ///   were implemented.
661    /// </summary>
662    public bool VerifyPendingMethods ()
663    {
664      int top = pending_implementations.Length;
665      bool errors = false;
666      int i;
667     
668      for (i = 0; i < top; i++){
669        TypeSpec type = pending_implementations [i].type;
670
671        bool base_implements_type = type.IsInterface &&
672          container.BaseType != null &&
673          container.BaseType.ImplementsInterface (type, false);
674
675        for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
676          var mi = pending_implementations[i].methods[j];
677          if (mi == null)
678            continue;
679
680          if (type.IsInterface){
681            var need_proxy =
682              pending_implementations [i].need_proxy [j];
683
684            if (need_proxy != null) {
685              DefineProxy (type, need_proxy, mi);
686              continue;
687            }
688
689            if (pending_implementations [i].optional)
690              continue;
691
692            MethodSpec candidate;
693            if (base_implements_type || BaseImplements (type, mi, out candidate))
694              continue;
695
696            if (candidate == null) {
697              MethodData md = pending_implementations [i].found [j];
698              if (md != null)
699                candidate = md.method.Spec;
700            }
701
702            Report.SymbolRelatedToPreviousError (mi);
703            if (candidate != null) {
704              Report.SymbolRelatedToPreviousError (candidate);
705              if (candidate.IsStatic) {
706                Report.Error (736, container.Location,
707                  "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
708                  container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
709              } else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) {
710                Report.Error (737, container.Location,
711                  "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is not public",
712                  container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
713              } else {
714                Report.Error (738, container.Location,
715                  "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
716                  container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (),
717                  candidate.ReturnType.GetSignatureForError (), mi.ReturnType.GetSignatureForError ());
718              }
719            } else {
720              Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
721                container.GetSignatureForError (), mi.GetSignatureForError ());
722            }
723          } else {
724            Report.SymbolRelatedToPreviousError (mi);
725            Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
726              container.GetSignatureForError (), mi.GetSignatureForError ());
727          }
728          errors = true;
729        }
730      }
731      return errors;
732    }
733  }
734}
Note: See TracBrowser for help on using the repository browser.