2001-11-22 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mcs / mcs / delegate.cs
1 //\r
2 // delegate.cs: Delegate Handler\r
3 //\r
4 // Author: Ravi Pratap (ravi@ximian.com)\r
5 //\r
6 // Licensed under the terms of the GNU GPL\r
7 //\r
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)\r
9 //\r
10 //\r
11 \r
12 using System;\r
13 using System.Collections;\r
14 using System.Reflection;\r
15 using System.Reflection.Emit;\r
16 using System.Text;\r
17 \r
18 namespace Mono.CSharp {\r
19 \r
20         /// <summary>\r
21         ///   Holds Delegates\r
22         /// </summary>\r
23         public class Delegate {\r
24 \r
25                 public readonly string Name;\r
26                 public readonly string ReturnType;\r
27                 public int             mod_flags;\r
28                 public Parameters      Parameters;\r
29                 public Attributes      OptAttributes;\r
30                 public TypeBuilder     TypeBuilder;\r
31 \r
32                 public ConstructorBuilder ConstructorBuilder;\r
33                 public MethodBuilder      InvokeBuilder;\r
34                 public MethodBuilder      BeginInvokeBuilder;\r
35                 public MethodBuilder      EndInvokeBuilder;\r
36                 \r
37                 public readonly RootContext RootContext;\r
38 \r
39                 Type [] param_types;\r
40                 Type ret_type;\r
41                 \r
42                 Expression instance_expr;\r
43                 MethodBase delegate_method;\r
44         \r
45                 Location loc;\r
46 \r
47                 const int AllowedModifiers =\r
48                         Modifiers.NEW |\r
49                         Modifiers.PUBLIC |\r
50                         Modifiers.PROTECTED |\r
51                         Modifiers.INTERNAL |\r
52                         Modifiers.PRIVATE;\r
53 \r
54                 public Delegate (RootContext rc, string type, int mod_flags, string name, Parameters param_list,\r
55                                  Attributes attrs, Location loc)\r
56                 {\r
57                         this.RootContext = rc;\r
58                         this.Name       = name;\r
59                         this.ReturnType = type;\r
60                         this.mod_flags  = Modifiers.Check (AllowedModifiers, mod_flags, Modifiers.PUBLIC);\r
61                         Parameters      = param_list;\r
62                         OptAttributes   = attrs;\r
63                         this.loc        = loc;\r
64                 }\r
65 \r
66                 public void DefineDelegate (object parent_builder)\r
67                 {\r
68                         TypeAttributes attr;\r
69                         \r
70                         if (parent_builder is ModuleBuilder) {\r
71                                 ModuleBuilder builder = (ModuleBuilder) parent_builder;\r
72                                 \r
73                                 attr = TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed;\r
74 \r
75                                 TypeBuilder = builder.DefineType (Name, attr, TypeManager.delegate_type);\r
76                                                                   \r
77                         } else {\r
78                                 TypeBuilder builder = (TypeBuilder) parent_builder;\r
79                                 \r
80                                 attr = TypeAttributes.NestedPublic | TypeAttributes.Class | TypeAttributes.Sealed;\r
81 \r
82                                 TypeBuilder = builder.DefineNestedType (Name, attr, TypeManager.delegate_type);\r
83 \r
84                         }\r
85 \r
86                         RootContext.TypeManager.AddDelegateType (Name, TypeBuilder, this);\r
87                 }\r
88 \r
89                 public void Populate (TypeContainer parent)\r
90                 {\r
91 \r
92                         MethodAttributes mattr;\r
93                         int i;\r
94                         \r
95                         Type [] const_arg_types = new Type [2];\r
96 \r
97                         const_arg_types [0] = TypeManager.object_type;\r
98                         const_arg_types [1] = TypeManager.intptr_type;\r
99 \r
100                         mattr = MethodAttributes.RTSpecialName | MethodAttributes.SpecialName |\r
101                                 MethodAttributes.HideBySig | MethodAttributes.Public;\r
102 \r
103                         ConstructorBuilder = TypeBuilder.DefineConstructor (mattr,\r
104                                                                             CallingConventions.Standard,\r
105                                                                             const_arg_types);\r
106                         \r
107                         ConstructorBuilder.SetImplementationFlags (MethodImplAttributes.Runtime);\r
108                         \r
109                         // Here the various methods like Invoke, BeginInvoke etc are defined\r
110 \r
111                         param_types = Parameters.GetParameterInfo (parent);\r
112                         ret_type = parent.LookupType (ReturnType, false);\r
113                         CallingConventions cc = Parameters.GetCallingConvention ();\r
114 \r
115                         mattr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual;\r
116 \r
117                         InvokeBuilder = TypeBuilder.DefineMethod ("Invoke", \r
118                                                                   mattr,                     \r
119                                                                   cc,\r
120                                                                   ret_type,                  \r
121                                                                   param_types);\r
122 \r
123                         for (i = 0 ; i < param_types.Length; i++) {\r
124                                 Parameter p = Parameters.FixedParameters [i];\r
125                                 string name = p.Name;\r
126                                 ParameterBuilder pb = InvokeBuilder.DefineParameter (i+1, p.Attributes, name); \r
127                         }\r
128                         \r
129                         InvokeBuilder.SetImplementationFlags (MethodImplAttributes.Runtime);\r
130 \r
131                         TypeContainer.RegisterParameterForBuilder (InvokeBuilder,\r
132                                                                    new InternalParameters (parent, Parameters)); \r
133 \r
134                         int params_num = param_types.Length;\r
135                         Type [] async_param_types = new Type [params_num + 2];\r
136 \r
137                         param_types.CopyTo (async_param_types, 0);\r
138 \r
139                         async_param_types [params_num] = TypeManager.asynccallback_type;\r
140                         async_param_types [params_num + 1] = TypeManager.object_type;\r
141 \r
142                         mattr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual |\r
143                                 MethodAttributes.NewSlot;\r
144                         \r
145                         BeginInvokeBuilder = TypeBuilder.DefineMethod ("BeginInvoke",\r
146                                                                        mattr,\r
147                                                                        cc,\r
148                                                                        TypeManager.iasyncresult_type,\r
149                                                                        async_param_types);\r
150 \r
151                         for (i = 0 ; i < param_types.Length; i++) {\r
152                                 Parameter p = Parameters.FixedParameters [i];\r
153                                 string name = p.Name;\r
154                                 BeginInvokeBuilder.DefineParameter (i + 1, p.Attributes, name); \r
155                         }\r
156                         \r
157                         BeginInvokeBuilder.DefineParameter (i + 1, ParameterAttributes.None, "callback");\r
158                         BeginInvokeBuilder.DefineParameter (i + 2, ParameterAttributes.None, "object");\r
159                         \r
160                         BeginInvokeBuilder.SetImplementationFlags (MethodImplAttributes.Runtime);\r
161 \r
162                         Parameter [] async_params = new Parameter [params_num + 2];\r
163                         Parameters.FixedParameters.CopyTo (async_params, 0);\r
164 \r
165                         async_params [params_num] = new Parameter ("System.AsyncCallback", "callback",\r
166                                                                    Parameter.Modifier.NONE, null);\r
167                         async_params [params_num + 1] = new Parameter ("System.IAsyncResult", "object",\r
168                                                                    Parameter.Modifier.NONE, null);\r
169                         \r
170                         TypeContainer.RegisterParameterForBuilder (BeginInvokeBuilder,\r
171                                         new InternalParameters (parent, new Parameters (async_params, null))); \r
172 \r
173                         Type [] end_param_types = new Type [1];\r
174                         end_param_types [0] = TypeManager.iasyncresult_type;\r
175                         \r
176                         EndInvokeBuilder = TypeBuilder.DefineMethod ("EndInvoke",\r
177                                                                      mattr,\r
178                                                                      cc,\r
179                                                                      ret_type,\r
180                                                                      end_param_types);\r
181                         EndInvokeBuilder.DefineParameter (1, ParameterAttributes.None, "result");\r
182                         \r
183                         EndInvokeBuilder.SetImplementationFlags (MethodImplAttributes.Runtime);\r
184 \r
185                         Parameter [] end_params = new Parameter [1];\r
186                         end_params [0] = new Parameter ("System.IAsyncResult", "result",\r
187                                                         Parameter.Modifier.NONE, null);\r
188 \r
189                         TypeContainer.RegisterParameterForBuilder (EndInvokeBuilder,\r
190                                         new InternalParameters (parent, new Parameters (end_params, null))); \r
191                         \r
192                 }\r
193 \r
194                 // <summary>\r
195                 //  Verifies whether the method in question is compatible with the delegate\r
196                 //  Returns the method itself if okay and null if not.\r
197                 // </summary>\r
198                 public MethodBase VerifyMethod (MethodBase mb, Location loc)\r
199                 {\r
200                         ParameterData pd = Invocation.GetParameterData (mb);\r
201 \r
202                         bool mismatch = false;\r
203                         for (int i = param_types.Length; i > 0; ) {\r
204                                 i--;\r
205 \r
206                                 if (param_types [i] == pd.ParameterType (i))\r
207                                         continue;\r
208                                 else {\r
209                                         mismatch = true;\r
210                                         break;\r
211                                 }\r
212                         }\r
213 \r
214                         if (mismatch) {\r
215                                 Report.Error (123, loc, "Method '" + Invocation.FullMethodDesc (mb) + "' does not match " +\r
216                                               "delegate '" + FullDelegateDesc () + "'");\r
217                                 return null;\r
218                         }\r
219 \r
220                         if (ret_type == ((MethodInfo) mb).ReturnType)\r
221                                 return mb;\r
222                         else\r
223                                 mismatch = true;\r
224 \r
225                         if (mismatch) {\r
226                                 Report.Error (123, loc, "Method '" + Invocation.FullMethodDesc (mb) + "' does not match " +\r
227                                               "delegate '" + FullDelegateDesc () + "'");\r
228                                 return null;\r
229                         }\r
230 \r
231                         return null;\r
232                 }\r
233 \r
234                 // <summary>\r
235                 //  Verifies whether the invocation arguments are compatible with the\r
236                 //  delegate's target method\r
237                 // </summary>\r
238                 public bool VerifyApplicability (EmitContext ec, ArrayList args, Location loc)\r
239                 {\r
240                         int arg_count;\r
241 \r
242                         if (args == null)\r
243                                 arg_count = 0;\r
244                         else\r
245                                 arg_count = args.Count;\r
246                         \r
247                         if (param_types.Length != arg_count) {\r
248                                 Report.Error (1593, loc,\r
249                                               "Delegate '" + Name + "' does not take '" + arg_count + "' arguments");\r
250                                 return false;\r
251                         }\r
252 \r
253                         for (int i = arg_count; i > 0;) {\r
254                                 i--;\r
255                                 Expression conv;\r
256                                 Argument a = (Argument) args [i];\r
257                                 Expression a_expr = a.Expr;\r
258                                 \r
259                                 if (param_types [i] != a_expr.Type) {\r
260                                         \r
261                                         conv = Expression.ConvertImplicitStandard (ec, a_expr, param_types [i], loc);\r
262 \r
263                                         if (conv == null) {\r
264                                                 Report.Error (1594, loc,\r
265                                                               "Delegate '" + Name + "' has some invalid arguments.");\r
266 \r
267                                                 Report.Error (1503, loc,\r
268                                                        "Argument " + (i+1) +\r
269                                                        ": Cannot convert from '" +\r
270                                                        TypeManager.CSharpName (a_expr.Type)\r
271                                                        + "' to '" + TypeManager.CSharpName (param_types [i]) + "'");\r
272                                                 return false;\r
273                                         }\r
274 \r
275                                         if (a_expr != conv)\r
276                                                 a.Expr = conv;\r
277                                 }\r
278                         }\r
279 \r
280                         return true;\r
281                 }\r
282 \r
283                 /// <summary>\r
284                 ///  Verifies whether the delegate in question is compatible with this one in\r
285                 ///  order to determine if instantiation from the same is possible.\r
286                 /// </summary>\r
287                 public bool VerifyDelegate (Delegate del)\r
288                 {\r
289                         if (ret_type != del.TargetReturnType)\r
290                                 return false;\r
291 \r
292                         Type [] other_param_types = del.ParameterTypes;\r
293                         \r
294                         if (param_types.Length != other_param_types.Length)\r
295                                 return false;\r
296 \r
297                         for (int i = param_types.Length; i > 0; ) {\r
298                                 i--;\r
299 \r
300                                 if (param_types [i] != other_param_types [i])\r
301                                         return false;\r
302                         }\r
303 \r
304                         // FIXME : Hey, what about parameter modifiers ?\r
305 \r
306                         return true;\r
307 \r
308                 }\r
309                 \r
310                 public string FullDelegateDesc ()\r
311                 {\r
312                         StringBuilder sb = new StringBuilder (TypeManager.CSharpName (System.Type.GetType (ReturnType)));\r
313                         \r
314                         sb.Append (" " + Name);\r
315                         sb.Append (" (");\r
316 \r
317                         int length = param_types.Length;\r
318                         \r
319                         for (int i = length; i > 0; ) {\r
320                                 i--;\r
321                                 \r
322                                 sb.Append (TypeManager.CSharpName (param_types [length - i - 1]));\r
323                                 if (i != 0)\r
324                                         sb.Append (", ");\r
325                         }\r
326                         \r
327                         sb.Append (")");\r
328                         return sb.ToString ();\r
329                         \r
330                 }\r
331                 \r
332                 // Hack around System.Reflection as found everywhere else\r
333                 public MemberInfo [] FindMembers (MemberTypes mt, BindingFlags bf, MemberFilter filter, object criteria)\r
334                 {\r
335                         ArrayList members = new ArrayList ();\r
336 \r
337                         if ((mt & MemberTypes.Method) != 0) {\r
338                                 if (filter (ConstructorBuilder, criteria))\r
339                                         members.Add (ConstructorBuilder);\r
340 \r
341                                 if (filter (InvokeBuilder, criteria))\r
342                                         members.Add (InvokeBuilder);\r
343 \r
344                                 if (filter (BeginInvokeBuilder, criteria))\r
345                                         members.Add (BeginInvokeBuilder);\r
346 \r
347                                 if (filter (EndInvokeBuilder, criteria))\r
348                                         members.Add (EndInvokeBuilder);\r
349                         }\r
350 \r
351                         int count = members.Count;\r
352 \r
353                         if (count > 0) {\r
354                                 MemberInfo [] mi = new MemberInfo [count];\r
355                                 members.CopyTo (mi, 0);\r
356                                 return mi;\r
357                         }\r
358 \r
359                         return null;\r
360                 }\r
361                 \r
362                 public void CloseDelegate ()\r
363                 {\r
364                         TypeBuilder.CreateType ();\r
365                 }\r
366                 \r
367                 public int ModFlags {\r
368                         get {\r
369                                 return mod_flags;\r
370                         }\r
371                 }\r
372 \r
373                 public Expression InstanceExpression {\r
374                         get {\r
375                                 return instance_expr;\r
376                         }\r
377                         set {\r
378                                 instance_expr = value;\r
379                         }\r
380                 }\r
381 \r
382                 public MethodBase TargetMethod {\r
383                         get {\r
384                                 return delegate_method;\r
385                         }\r
386                         set {\r
387                                 delegate_method = value;\r
388                         }\r
389                 }\r
390 \r
391                 public Type TargetReturnType {\r
392                         get {\r
393                                 return ret_type;\r
394                         }\r
395                 }\r
396 \r
397                 public Type [] ParameterTypes {\r
398                         get {\r
399                                 return param_types;\r
400                         }\r
401                 }\r
402                 \r
403         }\r
404 \r
405         public class NewDelegate : Expression {\r
406 \r
407                 public ArrayList Arguments;\r
408 \r
409                 MethodBase constructor_method;\r
410                 MethodBase delegate_method;\r
411                 Expression delegate_instance_expr;\r
412 \r
413                 Location Location;\r
414                 \r
415                 public NewDelegate (Type type, ArrayList Arguments, Location loc)\r
416                 {\r
417                         this.type = type;\r
418                         this.Arguments = Arguments;\r
419                         this.Location  = loc; \r
420                 }\r
421 \r
422                 public override Expression DoResolve (EmitContext ec)\r
423                 {\r
424                         Delegate del = TypeManager.LookupDelegate (type);\r
425                         constructor_method = del.ConstructorBuilder;\r
426                         \r
427                         if (Arguments == null) {\r
428                                 Report.Error (-11, Location,\r
429                                               "Delegate creation expression takes only one argument");\r
430                                 return null;\r
431                         }\r
432                         \r
433                         if (Arguments.Count != 1) {\r
434                                 Report.Error (-11, Location,\r
435                                               "Delegate creation expression takes only one argument");\r
436                                 return null;\r
437                         }\r
438                         \r
439                         Argument a = (Argument) Arguments [0];\r
440                         \r
441                         if (!a.Resolve (ec, Location))\r
442                                 return null;\r
443                         \r
444                         Expression e = a.Expr;\r
445                         \r
446                         if (e is MethodGroupExpr) {\r
447                                 MethodGroupExpr mg = (MethodGroupExpr) e;\r
448                                 \r
449                                 delegate_method  = del.VerifyMethod (mg.Methods [0], Location);\r
450                                 \r
451                                 if (delegate_method == null)\r
452                                         return null;\r
453                                 \r
454                                 if (mg.InstanceExpression != null)\r
455                                         delegate_instance_expr = mg.InstanceExpression.Resolve (ec);\r
456                                 else\r
457                                         delegate_instance_expr = null;\r
458                                 \r
459                                 if (delegate_instance_expr != null)\r
460                                         if (delegate_instance_expr.Type.IsValueType)\r
461                                                 delegate_instance_expr = new BoxedCast (delegate_instance_expr);\r
462                                 \r
463                                 \r
464                                 del.InstanceExpression = delegate_instance_expr;\r
465                                 del.TargetMethod = delegate_method;\r
466                                 \r
467                                 eclass = ExprClass.Value;\r
468                                 return this;\r
469                         }\r
470 \r
471                         Type e_type = e.Type;\r
472 \r
473                         Delegate d = TypeManager.LookupDelegate (e_type);\r
474 \r
475                         if (d == null) {\r
476                                 Report.Error (-12, Location, "Cannot create a delegate from something " +\r
477                                               "not a delegate or a method.");\r
478                                 return null;\r
479                         }\r
480 \r
481                         // This is what MS's compiler reports. We could always choose\r
482                         // to be more verbose and actually give delegate-level specifics\r
483                         \r
484                         if (!d.VerifyDelegate (del)) {\r
485                                 Report.Error (29, Location, "Cannot implicitly convert type '" + d.Name + "' " +\r
486                                               "to type '" + del.Name + "'");\r
487                                 return null;\r
488                         }\r
489 \r
490                         delegate_instance_expr = d.InstanceExpression;\r
491                         delegate_method        = d.TargetMethod;\r
492                         \r
493                         del.InstanceExpression = d.InstanceExpression;\r
494                         del.TargetMethod       = d.TargetMethod;\r
495                         \r
496                         eclass = ExprClass.Value;\r
497                         return this;\r
498                 }\r
499                 \r
500                 public override void Emit (EmitContext ec)\r
501                 {\r
502                         if (delegate_instance_expr == null)\r
503                                 ec.ig.Emit (OpCodes.Ldnull);\r
504                         else\r
505                                 delegate_instance_expr.Emit (ec);\r
506                         \r
507                         ec.ig.Emit (OpCodes.Ldftn, (MethodInfo) delegate_method);\r
508                         ec.ig.Emit (OpCodes.Newobj, (ConstructorInfo) constructor_method);\r
509                 }\r
510         }\r
511 \r
512         public class DelegateInvocation : ExpressionStatement {\r
513 \r
514                 public Expression InstanceExpr;\r
515                 public ArrayList  Arguments;\r
516                 public Location   Location;\r
517 \r
518                 MethodBase method;\r
519                 \r
520                 public DelegateInvocation (Expression instance_expr, ArrayList args, Location loc)\r
521                 {\r
522                         this.InstanceExpr = instance_expr;\r
523                         this.Arguments = args;\r
524                         this.Location = loc;\r
525                 }\r
526 \r
527                 public override Expression DoResolve (EmitContext ec)\r
528                 {\r
529                         Delegate del = TypeManager.LookupDelegate (InstanceExpr.Type);\r
530 \r
531                         if (del == null)\r
532                                 return null;\r
533 \r
534                         if (del.TargetMethod == null)\r
535                                 return null;\r
536                         \r
537                         if (Arguments != null){\r
538                                 for (int i = Arguments.Count; i > 0;){\r
539                                         --i;\r
540                                         Argument a = (Argument) Arguments [i];\r
541                                         \r
542                                         if (!a.Resolve (ec, Location))\r
543                                                 return null;\r
544                                 }\r
545                         }\r
546                         \r
547                         if (!del.VerifyApplicability (ec, Arguments, Location))\r
548                                 return null;\r
549                         \r
550                         method = del.InvokeBuilder;\r
551                         type = ((MethodInfo) method).ReturnType;\r
552                         \r
553                         eclass = ExprClass.Value;\r
554                         \r
555                         return this;\r
556                 }\r
557 \r
558                 public override void Emit (EmitContext ec)\r
559                 {\r
560                         Delegate del = TypeManager.LookupDelegate (InstanceExpr.Type);\r
561 \r
562                         //\r
563                         // Invocation on delegates call the virtual Invoke member\r
564                         // so we are always `instance' calls\r
565                         //\r
566                         Invocation.EmitCall (ec, false, InstanceExpr, method, Arguments);\r
567                 }\r
568 \r
569                 public override void EmitStatement (EmitContext ec)\r
570                 {\r
571                         Emit (ec);\r
572                         // \r
573                         // Pop the return value if there is one\r
574                         //\r
575                         if (method is MethodInfo){\r
576                                 if (((MethodInfo) method).ReturnType != TypeManager.void_type)\r
577                                         ec.ig.Emit (OpCodes.Pop);\r
578                         }\r
579                 }\r
580 \r
581         }\r
582 }\r