Optional parameters support
[mono.git] / mcs / mbas / parameter.cs
1 //
2 // parameter.cs: Parameter definition.
3 //
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 //
6 // Licensed under the terms of the GNU GPL
7 //
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)
9 //
10 //
11 //
12 using System;
13 using System.Reflection;
14 using System.Reflection.Emit;
15 using System.Collections;
16
17 namespace Mono.CSharp {
18
19
20         /// <summary>
21         ///   Represents a single method parameter
22         /// </summary>
23         public class Parameter {
24                 [Flags]
25                 public enum Modifier : byte {
26                         NONE    = 0,
27                         VAL     = 0,
28                         REF     = 1,
29                         OUT     = 2,
30                         PARAMS  = 4,
31                         // This is a flag which says that it's either REF or OUT.
32                         ISBYREF = 8,
33                         OPTIONAL = 16
34                 }
35
36                 public readonly Expression TypeName;
37                 public readonly Modifier ModFlags;
38                 public Attributes OptAttributes;
39                 public readonly string Name;
40                 public Type parameter_type;
41                 public readonly Expression ParameterInitializer;
42                 public readonly bool IsOptional;
43
44                 public Parameter (Expression type, string name, Modifier mod, Attributes attrs)
45                 {
46                         Name = name;
47                         ModFlags = mod;
48                         TypeName = type;
49                         OptAttributes = attrs;
50                         ParameterInitializer = null;
51                         IsOptional = false;
52                 }
53
54                 public Parameter (Expression type, string name, Modifier mod, Attributes attrs, Expression pi)
55                 {
56                         Name = name;
57                         ModFlags = mod;
58                         TypeName = type;
59                         OptAttributes = attrs;
60                         ParameterInitializer = pi;
61                         IsOptional = false;
62                 }
63                 
64                 public Parameter (Expression type, string name, Modifier mod, Attributes attrs, Expression pi, bool opt)
65                 {
66                         Name = name;
67                         ModFlags = mod;
68                         TypeName = type;
69                         OptAttributes = attrs;
70                         ParameterInitializer = pi;
71                         IsOptional = opt;
72                 }
73                 
74
75                 // <summary>
76                 //   Resolve is used in method definitions
77                 // </summary>
78                 public bool Resolve (DeclSpace ds, Location l)
79                 {
80                         parameter_type = ds.ResolveType (TypeName, false, l);
81
82                         if (parameter_type == TypeManager.void_type){
83                                 Report.Error (1536, l, "`void' parameter is not permitted");
84                                 return false;
85                         }
86                         
87                         return parameter_type != null;
88                 }
89
90                 public Type ExternalType (DeclSpace ds, Location l)
91                 {
92                         if ((ModFlags & Parameter.Modifier.ISBYREF) != 0){
93                                 string n = parameter_type.FullName + "&";
94
95                                 Type t = RootContext.LookupType (ds, n, false, l);
96
97                                 return t;
98                         }
99                         
100                         return parameter_type;
101                 }
102
103                 public Type ParameterType {
104                         get {
105                                 return parameter_type;
106                         }
107                 }
108                 
109                 public ParameterAttributes Attributes {
110                         get {
111                                 int flags = ((int) ModFlags) & ~((int) Parameter.Modifier.ISBYREF);
112                                 switch ((Modifier) flags) {
113                                 case Modifier.NONE:
114                                         return ParameterAttributes.None;
115                                 case Modifier.REF:
116                                         return ParameterAttributes.None;
117                                 case Modifier.OUT:
118                                         return ParameterAttributes.Out;
119                                 case Modifier.PARAMS:
120                                         return 0;
121                                 }
122                                 
123                                 return ParameterAttributes.None;
124                         }
125                 }
126                 
127                 /// <summary>
128                 ///   Returns the signature for this parameter evaluating it on the
129                 ///   @tc context
130                 /// </summary>
131                 public string GetSignature (DeclSpace ds, Location loc)
132                 {
133                         if (parameter_type == null){
134                                 if (!Resolve (ds, loc))
135                                         return null;
136                         }
137
138                         return ExternalType (ds, loc).FullName;
139                 }
140         }
141
142         /// <summary>
143         ///   Represents the methods parameters
144         /// </summary>
145         public class Parameters {
146                 public Parameter [] FixedParameters;
147                 public readonly Parameter ArrayParameter;
148                 string signature;
149                 Type [] types;
150                 Location loc;
151                 
152                 static Parameters empty_parameters;
153                 
154                 public Parameters (Parameter [] fixed_parameters, Parameter array_parameter, Location l)
155                 {
156                         FixedParameters = fixed_parameters;
157                         ArrayParameter  = array_parameter;
158                         loc = l;
159                 }
160         
161                 /// <summary>
162                 ///   This is used to reuse a set of empty parameters, because they
163                 ///   are common
164                 /// </summary>
165                 public static Parameters EmptyReadOnlyParameters {
166                         get {
167                                 if (empty_parameters == null)
168                                         empty_parameters = new Parameters (null, null, Location.Null);
169                         
170                                 return empty_parameters;
171                         }
172                 }
173                 
174                 public bool HasOptional()
175                 {
176                         bool res = false;
177
178                         foreach (Parameter p in FixedParameters) 
179                         {
180                                 if (p.IsOptional) 
181                                 {
182                                         res = true;
183                                         break;
184                                 }
185                         }
186                         return (res);
187                 }
188                 
189                 /// <summary>
190                 ///   Returns the number of standard (i.e. non-optional) parameters
191                 /// </summary>          
192                 public int CountStandardParams()
193                 {
194                         int res = 0;
195                         
196                         foreach (Parameter p in FixedParameters) {
197                                 if (!p.IsOptional)
198                                         res++;
199                         }
200                         return (res);
201                 }
202                 
203                 /// <summary>
204                 ///   Returns the number of optional parameters
205                 /// </summary>          
206                 public int CountOptionalParams()
207                 {
208                         int res = 0;
209                         
210                         foreach (Parameter p in FixedParameters) {
211                                 if (p.IsOptional)
212                                         res++;
213                         }
214                         return (res);
215                 }               
216
217                 public Expression GetDefaultValue (int i)
218                 {
219                         Parameter p = FixedParameters[i];
220                         if (p.IsOptional)
221                                 return p.ParameterInitializer;
222                         else
223                                 return null;
224                 }
225
226                 public void AppendParameter (Parameter p)
227                 {
228                         if (FixedParameters != null) 
229                         {
230                                 Parameter [] pa = new Parameter [FixedParameters.Length+1];
231                                 FixedParameters.CopyTo (pa, 0);
232                                 pa[FixedParameters.Length] = p;
233                                 FixedParameters = pa;           
234                         }
235                         else
236                         {
237                                 FixedParameters = new Parameter [1];
238                                 FixedParameters[0] = p;
239                         }
240                 }
241
242                 public void PrependParameter (Parameter p)
243                 {
244                         Parameter [] pa = new Parameter [FixedParameters.Length+1];
245                         FixedParameters.CopyTo (pa, 1);
246                         pa[0] = p;
247                         FixedParameters = pa;           
248                 }
249                 
250                 public Parameters Copy (Location l)
251                 {
252                         Parameters p = new Parameters (null, null, l);
253                         p.FixedParameters = new Parameter[this.FixedParameters.Length];
254                         this.FixedParameters.CopyTo (p.FixedParameters, 0);
255                         
256                         return (p);
257                         
258                 }
259                 
260                 public bool Empty {
261                         get {
262                                 return (FixedParameters == null) && (ArrayParameter == null);
263                         }
264                 }
265                 
266                 public void ComputeSignature (DeclSpace ds)
267                 {
268                         signature = "";
269                         if (FixedParameters != null){
270                                 for (int i = 0; i < FixedParameters.Length; i++){
271                                         Parameter par = FixedParameters [i];
272                                         
273                                         signature += par.GetSignature (ds, loc);
274                                 }
275                         }
276                         //
277                         // Note: as per the spec, the `params' arguments (ArrayParameter)
278                         // are not used in the signature computation for a method
279                         //
280                 }
281
282                 static void Error_DuplicateParameterName (string name)
283                 {
284                         Report.Error (
285                                 100, "The parameter name `" + name + "' is a duplicate");
286                 }
287                 
288                 public bool VerifyArgs ()
289                 {
290                         int count;
291                         int i, j;
292
293                         if (FixedParameters == null)
294                                 return true;
295                         
296                         count = FixedParameters.Length;
297                         string array_par_name = ArrayParameter != null ? ArrayParameter.Name : null;
298                         for (i = 0; i < count; i++){
299                                 string base_name = FixedParameters [i].Name;
300                                 
301                                 for (j = i + 1; j < count; j++){
302                                         if (base_name != FixedParameters [j].Name)
303                                                 continue;
304                                         Error_DuplicateParameterName (base_name);
305                                         return false;
306                                 }
307
308                                 if (base_name == array_par_name){
309                                         Error_DuplicateParameterName (base_name);
310                                         return false;
311                                 }
312                         }
313                         return true;
314                 }
315                 
316                 /// <summary>
317                 ///    Returns the signature of the Parameters evaluated in
318                 ///    the @tc environment
319                 /// </summary>
320                 public string GetSignature (DeclSpace ds)
321                 {
322                         if (signature == null){
323                                 VerifyArgs ();
324                                 ComputeSignature (ds);
325                         }
326                         
327                         return signature;
328                 }
329                 
330                 /// <summary>
331                 ///    Returns the paramenter information based on the name
332                 /// </summary>
333                 public Parameter GetParameterByName (string name, out int idx)
334                 {
335                         idx = 0;
336                         int i = 0;
337
338                         if (FixedParameters != null){
339                                 foreach (Parameter par in FixedParameters){
340                                         if (par.Name == name){
341                                                 idx = i;
342                                                 return par;
343                                         }
344                                         i++;
345                                 }
346                         }
347
348                         if (ArrayParameter != null){
349                                 if (name == ArrayParameter.Name){
350                                         idx = i;
351                                         return ArrayParameter;
352                                 }
353                         }
354                         
355                         return null;
356                 }
357
358                 bool ComputeParameterTypes (DeclSpace ds)
359                 {
360                         int extra = (ArrayParameter != null) ? 1 : 0;
361                         int i = 0;
362                         int pc;
363
364                         if (FixedParameters == null)
365                                 pc = extra;
366                         else
367                                 pc = extra + FixedParameters.Length;
368
369                         types = new Type [pc];
370                         
371                         if (!VerifyArgs ()){
372                                 FixedParameters = null;
373                                 return false;
374                         }
375
376                         bool failed = false;
377                         if (FixedParameters != null){
378                                 foreach (Parameter p in FixedParameters){
379                                         Type t = null;
380                                         
381                                         if (p.Resolve (ds, loc))
382                                                 t = p.ExternalType (ds, loc);
383                                         else
384                                                 failed = true;
385                                         
386                                         types [i] = t;
387                                         i++;
388                                 }
389                         }
390                         
391                         if (extra > 0){
392                                 if (ArrayParameter.Resolve (ds, loc))
393                                         types [i] = ArrayParameter.ExternalType (ds, loc);
394                                 else 
395                                         failed = true;
396                         }
397
398                         if (failed){
399                                 types = null;
400                                 return false;
401                         }
402
403                         return true;
404                 }
405
406                 //
407                 // This variant is used by Delegates, because they need to
408                 // resolve/define names, instead of the plain LookupType
409                 //
410                 public bool ComputeAndDefineParameterTypes (DeclSpace ds)
411                 {
412                         int extra = (ArrayParameter != null) ? 1 : 0;
413                         int i = 0;
414                         int pc;
415
416                         if (FixedParameters == null)
417                                 pc = extra;
418                         else
419                                 pc = extra + FixedParameters.Length;
420                         
421                         types = new Type [pc];
422                         
423                         if (!VerifyArgs ()){
424                                 FixedParameters = null;
425                                 return false;
426                         }
427
428                         bool ok_flag = true;
429                         
430                         if (FixedParameters != null){
431                                 foreach (Parameter p in FixedParameters){
432                                         Type t = null;
433                                         
434                                         if (p.Resolve (ds, loc))
435                                                 t = p.ExternalType (ds, loc);
436                                         else
437                                                 ok_flag = false;
438                                         
439                                         types [i] = t;
440                                         i++;
441                                 }
442                         }
443                         
444                         if (extra > 0){
445                                 if (ArrayParameter.Resolve (ds, loc))
446                                         types [i] = ArrayParameter.ExternalType (ds, loc);
447                                 else
448                                         ok_flag = false;
449                         }
450
451                         //
452                         // invalidate the cached types
453                         //
454                         if (!ok_flag){
455                                 types = null;
456                         }
457                         
458                         return ok_flag;
459                 }
460                 
461                 /// <summary>
462                 ///   Returns the argument types as an array
463                 /// </summary>
464                 static Type [] no_types = new Type [0];
465                 
466                 public Type [] GetParameterInfo (DeclSpace ds)
467                 {
468                         if (types != null)
469                                 return types;
470                         
471                         if (FixedParameters == null && ArrayParameter == null)
472                                 return no_types;
473
474                         if (ComputeParameterTypes (ds) == false){
475                                 types = null;
476                                 return null;
477                         }
478
479                         return types;
480                 }
481
482                 /// <summary>
483                 ///   Returns the type of a given parameter, and stores in the `is_out'
484                 ///   boolean whether this is an out or ref parameter.
485                 ///
486                 ///   Note that the returned type will not contain any dereference in this
487                 ///   case (ie, you get "int" for a ref int instead of "int&"
488                 /// </summary>
489                 public Type GetParameterInfo (DeclSpace ds, int idx, out Parameter.Modifier mod)
490                 {
491                         mod = Parameter.Modifier.NONE;
492                         
493                         if (!VerifyArgs ()){
494                                 FixedParameters = null;
495                                 return null;
496                         }
497
498                         if (FixedParameters == null && ArrayParameter == null)
499                                 return null;
500                         
501                         if (types == null)
502                                 if (ComputeParameterTypes (ds) == false)
503                                         return null;
504
505                         //
506                         // If this is a request for the variable lenght arg.
507                         //
508                         int array_idx = (FixedParameters != null ? FixedParameters.Length : 0);
509                         if (idx == array_idx)
510                                 return types [idx];
511
512                         //
513                         // Otherwise, it is a fixed parameter
514                         //
515                         Parameter p = FixedParameters [idx];
516                         mod = p.ModFlags;
517
518                         if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0)
519                                 mod |= Parameter.Modifier.ISBYREF;
520
521                         return p.ParameterType;
522                 }
523
524                 public CallingConventions GetCallingConvention ()
525                 {
526                         // For now this is the only correc thing to do
527                         return CallingConventions.Standard;
528                 }
529         }
530 }
531                 
532         
533