Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Activities / System / Activities / Statements / MethodResolver.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.Activities.Statements
6 {
7     using System.Collections.Generic;
8     using System.Collections.ObjectModel;
9     using System.Globalization;
10     using System.Linq;
11     using System.Reflection;
12     using System.Runtime;
13     using System.Activities.Expressions;
14     using System.Threading;
15
16     // Helper class for InvokeMethod.
17     // Factory for MethodExecutor strategies. Conceptually, resolves to the correct MethodInfo based on target type,
18     // method name, parameters, and async flags + availability of Begin/End paired methods of the correct static-ness.
19     sealed class MethodResolver
20     {
21
22         static readonly BindingFlags staticBindingFlags = BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static;
23         static readonly BindingFlags instanceBindingFlags = BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance;
24         static readonly string staticString = "static";     // Used in error messages below. Technical term, not localizable.
25         static readonly string instanceString = "instance"; // Used in error messages below. Technical term, not localizable.
26         MethodInfo syncMethod;
27         MethodInfo beginMethod;
28         MethodInfo endMethod;
29
30         public MethodResolver()
31         {
32         }
33
34         public Collection<Type> GenericTypeArguments { get; set; }
35
36         public string MethodName { get; set; }
37
38         public Collection<Argument> Parameters { get; set; }
39
40         public RuntimeArgument Result { get; set; }
41
42         public InArgument TargetObject { get; set; }
43
44         public Type TargetType { get; set; }
45
46         public bool RunAsynchronously { get; set; }
47
48         public Activity Parent { get; set; }
49
50         // Sometimes we may know the result type even if it won't be used,
51         // i.e. it comes from an InvokeMethod<T>. We will want to generate
52         // errors if it doesn't match the method's return value. 
53         internal Type ResultType { get; set; }
54
55         static bool HaveParameterArray(ParameterInfo[] parameters)
56         {
57             if (parameters.Length > 0)
58             {
59                 ParameterInfo last = parameters[parameters.Length - 1];
60                 return last.GetCustomAttributes(typeof(ParamArrayAttribute), true).Length > 0;
61             }
62             else
63             {
64                 return false;
65             }
66         }
67
68         // The Arguments added by the activity are named according to the method resolved by the MethodResolver.
69         public void RegisterParameters(IList<RuntimeArgument> arguments)
70         {
71             bool useAsyncPattern = this.RunAsynchronously && this.beginMethod != null && this.endMethod != null;
72
73             if (this.syncMethod != null || useAsyncPattern)
74             {
75                 ParameterInfo[] formalParameters;
76                 int formalParamCount;
77                 string paramArrayBaseName = "";
78                 bool haveParameterArray = false;
79
80                 if (useAsyncPattern)
81                 {
82                     formalParameters = this.beginMethod.GetParameters();
83                     formalParamCount = formalParameters.Length - 2;
84                 }
85                 else
86                 {
87                     formalParameters = this.syncMethod.GetParameters();
88                     haveParameterArray = HaveParameterArray(formalParameters);
89
90                     if (haveParameterArray)
91                     {
92                         formalParamCount = formalParameters.Length - 1;
93                         paramArrayBaseName = formalParameters[formalParamCount].Name;
94                     }
95                     else
96                     {
97                         formalParamCount = formalParameters.Length;
98                     }
99                 }
100
101                 for (int i = 0; i < formalParamCount; i++)
102                 {
103                     string name = formalParameters[i].Name;
104                     //for some methods like int[,].Get(int,int), formal parameters have no names in reflection info
105                     if (string.IsNullOrEmpty(name))
106                     {
107                         name = "Parameter" + i;
108                     }
109
110                     RuntimeArgument argument = new RuntimeArgument(name, Parameters[i].ArgumentType, Parameters[i].Direction, true);
111                     Argument.Bind(Parameters[i], argument);
112                     arguments.Add(argument);
113
114                     if (!useAsyncPattern && haveParameterArray)
115                     {
116                         // Attempt to uniquify parameter names
117                         if (name.StartsWith(paramArrayBaseName, false, null))
118                         {
119                             int n;
120                             if (int.TryParse(name.Substring(paramArrayBaseName.Length), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out n))
121                             {
122                                 paramArrayBaseName += "_";
123                             }
124                         }
125                     }
126                 }
127
128                 if (!useAsyncPattern && haveParameterArray)
129                 {
130                     // RuntimeArgument bindings need names. In the case of params arrays, synthesize names based on the name of the formal params parameter
131                     // plus a counter.
132                     int paramArrayCount = Parameters.Count - formalParamCount;
133
134                     for (int i = 0; i < paramArrayCount; i++)
135                     {
136                         string name = paramArrayBaseName + i;
137                         int index = formalParamCount + i;
138                         RuntimeArgument argument = new RuntimeArgument(name, Parameters[index].ArgumentType, Parameters[index].Direction, true);
139                         Argument.Bind(Parameters[index], argument);
140                         arguments.Add(argument);
141                     }
142                 }
143             }
144             else
145             {
146                 // We're still at design-time: make up "fake" arguments based on the parameters
147                 for (int i = 0; i < Parameters.Count; i++)
148                 {
149                     string name = "argument" + i;
150                     RuntimeArgument argument = new RuntimeArgument(name, Parameters[i].ArgumentType, Parameters[i].Direction, true);
151                     Argument.Bind(Parameters[i], argument);
152                     arguments.Add(argument);
153                 }
154             }
155         }
156
157         public void Trace()
158         {
159             bool useAsyncPattern = this.RunAsynchronously && this.beginMethod != null && this.endMethod != null;
160
161             if (useAsyncPattern)
162             {
163                 if (TD.InvokeMethodUseAsyncPatternIsEnabled())
164                 {
165                     TD.InvokeMethodUseAsyncPattern(this.Parent.DisplayName, this.beginMethod.ToString(), this.endMethod.ToString());
166                 }
167             }
168             else
169             {
170                 if (this.RunAsynchronously)
171                 {
172                     if (TD.InvokeMethodDoesNotUseAsyncPatternIsEnabled())
173                     {
174                         TD.InvokeMethodDoesNotUseAsyncPattern(this.Parent.DisplayName);
175                     }
176                 }
177             }
178         }
179
180         // Set methodExecutor, returning an error string if there are any problems (ambiguous match, etc.).
181         public void DetermineMethodInfo(CodeActivityMetadata metadata, MruCache<MethodInfo, Func<object, object[], object>> funcCache, ReaderWriterLockSlim locker, 
182             ref MethodExecutor methodExecutor)
183         {
184             bool returnEarly = false;
185
186             MethodExecutor oldMethodExecutor = methodExecutor;
187             methodExecutor = null;
188             if (string.IsNullOrEmpty(this.MethodName))
189             {
190                 metadata.AddValidationError(SR.ActivityPropertyMustBeSet("MethodName", this.Parent.DisplayName));
191                 returnEarly = true;
192             }
193
194             Type targetType = this.TargetType;
195
196             // If TargetType and the type of TargetObject are both set, it's an error.
197             if (targetType != null && this.TargetObject != null && !this.TargetObject.IsEmpty)
198             {
199                 metadata.AddValidationError(SR.TargetTypeAndTargetObjectAreMutuallyExclusive(this.Parent.GetType().Name, this.Parent.DisplayName));
200                 returnEarly = true;
201             }
202
203             // If TargetType was set, look for a static method. If TargetObject was set, look for an instance method. They can't both be set.
204             BindingFlags bindingFlags = this.TargetType != null ? staticBindingFlags : instanceBindingFlags;
205             string bindingType = bindingFlags == staticBindingFlags ? staticString : instanceString;
206
207             if (targetType == null)
208             {
209                 if (this.TargetObject != null && !this.TargetObject.IsEmpty)
210                 {
211                     targetType = this.TargetObject.ArgumentType;
212                 }
213                 else
214                 {
215                     metadata.AddValidationError(SR.OneOfTwoPropertiesMustBeSet("TargetObject", "TargetType", this.Parent.GetType().Name, this.Parent.DisplayName));
216                     returnEarly = true;
217                 }
218             }
219
220             // We've had one or more constraint violations already
221             if (returnEarly)
222             {
223                 return;
224             }
225
226             // Convert OutArgs and InOutArgs to out/ref types before resolution
227             Type[] parameterTypes =
228                 Parameters.Select(argument => argument.Direction == ArgumentDirection.In ? argument.ArgumentType : argument.ArgumentType.MakeByRefType())
229                     .ToArray();
230
231             Type[] genericTypeArguments = this.GenericTypeArguments.ToArray();
232
233             InheritanceAndParamArrayAwareBinder methodBinder = new InheritanceAndParamArrayAwareBinder(targetType, genericTypeArguments, this.Parent);
234
235             // It may be possible to know (and check) the resultType even if the result won't be assigned anywhere.     
236             // Used 1.) for detecting async pattern, and 2.) to make sure we selected the correct MethodInfo.
237             Type resultType = this.ResultType;
238
239             if (this.RunAsynchronously)
240             {
241                 int formalParamCount = parameterTypes.Length;
242                 Type[] beginMethodParameterTypes = new Type[formalParamCount + 2];
243                 for (int i = 0; i < formalParamCount; i++)
244                 {
245                     beginMethodParameterTypes[i] = parameterTypes[i];
246                 }
247                 beginMethodParameterTypes[formalParamCount] = typeof(AsyncCallback);
248                 beginMethodParameterTypes[formalParamCount + 1] = typeof(object);
249
250                 Type[] endMethodParameterTypes = { typeof(IAsyncResult) };
251
252                 this.beginMethod = Resolve(targetType, "Begin" + this.MethodName, bindingFlags,
253                     methodBinder, beginMethodParameterTypes, genericTypeArguments, true);
254                 if (this.beginMethod != null && !this.beginMethod.ReturnType.Equals(typeof(IAsyncResult)))
255                 {
256                     this.beginMethod = null;
257                 }
258                 this.endMethod = Resolve(targetType, "End" + this.MethodName, bindingFlags,
259                     methodBinder, endMethodParameterTypes, genericTypeArguments, true);
260                 if (this.endMethod != null && resultType != null && !TypeHelper.AreTypesCompatible(this.endMethod.ReturnType, resultType))
261                 {
262                     metadata.AddValidationError(SR.ReturnTypeIncompatible(this.endMethod.ReturnType.Name, MethodName, targetType.Name, this.Parent.DisplayName, resultType.Name));
263                     this.endMethod = null;
264                     return;
265                 }
266
267                 if (this.beginMethod != null && this.endMethod != null && this.beginMethod.IsStatic == this.endMethod.IsStatic)
268                 {
269                     if (!(oldMethodExecutor is AsyncPatternMethodExecutor) ||
270                         !((AsyncPatternMethodExecutor)oldMethodExecutor).IsTheSame(this.beginMethod, this.endMethod))
271                     {
272                         methodExecutor = new AsyncPatternMethodExecutor(metadata, this.beginMethod, this.endMethod, this.Parent, 
273                             this.TargetType, this.TargetObject, this.Parameters, this.Result, funcCache, locker);
274                     }
275                     else
276                     {
277                         methodExecutor = new AsyncPatternMethodExecutor((AsyncPatternMethodExecutor)oldMethodExecutor, 
278                             this.TargetType, this.TargetObject, this.Parameters, this.Result);
279                     }
280                     return;
281                 }
282             }
283
284             MethodInfo result;
285             try
286             {
287                 result = Resolve(targetType, this.MethodName, bindingFlags,
288                     methodBinder, parameterTypes, genericTypeArguments, false);
289             }
290             catch (AmbiguousMatchException)
291             {
292                 metadata.AddValidationError(SR.DuplicateMethodFound(targetType.Name, bindingType, MethodName, this.Parent.DisplayName));
293                 return;
294             }
295
296             if (result == null)
297             {
298                 metadata.AddValidationError(SR.PublicMethodWithMatchingParameterDoesNotExist(targetType.Name, bindingType, MethodName, this.Parent.DisplayName));
299                 return;
300             }
301             else if (resultType != null && !TypeHelper.AreTypesCompatible(result.ReturnType, resultType))
302             {
303                 metadata.AddValidationError(
304                     SR.ReturnTypeIncompatible(result.ReturnType.Name, MethodName,
305                         targetType.Name, this.Parent.DisplayName, resultType.Name));
306                 return;
307             }
308             else
309             {
310                 this.syncMethod = result;
311                 if (this.RunAsynchronously)
312                 {
313                     if (!(oldMethodExecutor is AsyncWaitCallbackMethodExecutor) ||
314                         !((AsyncWaitCallbackMethodExecutor)oldMethodExecutor).IsTheSame(this.syncMethod))
315                     {
316                         methodExecutor = new AsyncWaitCallbackMethodExecutor(metadata, this.syncMethod, this.Parent, 
317                             this.TargetType, this.TargetObject, this.Parameters, this.Result, funcCache, locker);
318                     }
319                     else
320                     {
321                         methodExecutor = new AsyncWaitCallbackMethodExecutor((AsyncWaitCallbackMethodExecutor)oldMethodExecutor,
322                             this.TargetType, this.TargetObject, this.Parameters, this.Result);
323                     }
324
325                 }
326                 else if (!(oldMethodExecutor is SyncMethodExecutor) ||
327                     !((SyncMethodExecutor)oldMethodExecutor).IsTheSame(this.syncMethod))
328                 {
329                     methodExecutor = new SyncMethodExecutor(metadata, this.syncMethod, this.Parent, this.TargetType,
330                         this.TargetObject, this.Parameters, this.Result, funcCache, locker);
331                 }
332                 else
333                 {
334                     methodExecutor = new SyncMethodExecutor((SyncMethodExecutor)oldMethodExecutor, this.TargetType,
335                         this.TargetObject, this.Parameters, this.Result);
336                 }
337
338             }
339         }
340
341         // returns null MethodInfo on failure
342         MethodInfo Resolve(Type targetType, string methodName, BindingFlags bindingFlags,
343             InheritanceAndParamArrayAwareBinder methodBinder, Type[] parameterTypes, Type[] genericTypeArguments, bool suppressAmbiguityException)
344         {
345             MethodInfo method;
346             try
347             {
348                 methodBinder.SelectMethodCalled = false;
349                 method = targetType.GetMethod(methodName, bindingFlags,
350                     methodBinder, CallingConventions.Any, parameterTypes, null);
351             }
352             catch (AmbiguousMatchException)
353             {
354                 if (suppressAmbiguityException) // For Begin/End methods, ambiguity just means no match
355                 {
356                     return null;
357                 }
358                 else // For a regular sync method, ambiguity is distinct from no match and gets an explicit error message
359                 {
360                     throw;
361                 }
362             }
363
364             if (method != null && !methodBinder.SelectMethodCalled && genericTypeArguments.Length > 0)
365             // methodBinder is only used when there's more than one possible match, so method might still be generic
366             {
367                 method = Instantiate(method, genericTypeArguments); // if it fails because of e.g. constraints it will just become null
368             }
369             return method;
370         }
371
372         // returns null on failure instead of throwing an exception (okay because it's an internal method)
373         static MethodInfo Instantiate(MethodInfo method, Type[] genericTypeArguments)
374         {
375             if (method.ContainsGenericParameters && method.GetGenericArguments().Length == genericTypeArguments.Length)
376             {
377                 try
378                 {
379                     // Must be a MethodInfo because we've already filtered out constructors                            
380                     return ((MethodInfo)method).MakeGenericMethod(genericTypeArguments);
381                 }
382                 catch (ArgumentException)
383                 {
384                     // Constraint violations will throw this exception--don't add to candidates
385                     return null;
386                 }
387             }
388             else
389             {
390                 return null;
391             }
392         }
393
394
395         // Store information about a particular asynchronous method call so we can update out/ref parameters, know 
396         // when/what to return, etc.
397         class InvokeMethodInstanceData
398         {
399             public object TargetObject { get; set; }
400             public object[] ActualParameters { get; set; }
401             public object ReturnValue { get; set; }
402             public bool ExceptionWasThrown { get; set; }
403             public Exception Exception { get; set; }
404         }
405
406         class InheritanceAndParamArrayAwareBinder : Binder
407         {
408             Type[] genericTypeArguments;
409
410             Type declaringType; // Methods declared directly on this type are preferred, followed by methods on its parents, etc.
411
412             internal bool SelectMethodCalled; // If this binder is actually used in resolution, it gets to do things like instantiate methods.
413             // Set this flag to false before calling Type.GetMethod. Check this flag after.
414
415             Activity parentActivity; // Used for generating AmbiguousMatchException error message
416
417             public InheritanceAndParamArrayAwareBinder(Type declaringType, Type[] genericTypeArguments, Activity parentActivity)
418             {
419                 this.declaringType = declaringType;
420                 this.genericTypeArguments = genericTypeArguments;
421                 this.parentActivity = parentActivity;
422             }
423
424             public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture)
425             {
426                 throw FxTrace.Exception.AsError(new NotImplementedException());
427             }
428
429             public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state)
430             {
431                 throw FxTrace.Exception.AsError(new NotImplementedException());
432             }
433
434             public override object ChangeType(object value, Type type, CultureInfo culture)
435             {
436                 throw FxTrace.Exception.AsError(new NotImplementedException());
437             }
438
439             public override void ReorderArgumentArray(ref object[] args, object state)
440             {
441                 throw FxTrace.Exception.AsError(new NotImplementedException());
442             }
443
444             public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
445             {
446                 MethodBase[] methodCandidates;
447                 this.SelectMethodCalled = true;
448
449                 if (this.genericTypeArguments.Length > 0)
450                 {
451                     // Accept only generic methods which can be successfully instantiated w/ these parameters
452                     Collection<MethodBase> methods = new Collection<MethodBase>();
453                     foreach (MethodBase method in match)
454                     {
455                         // Must be a MethodInfo because we've already filtered out constructors                            
456                         MethodInfo instantiatedMethod = Instantiate((MethodInfo)method, this.genericTypeArguments);
457                         if (instantiatedMethod != null)
458                         {
459                             methods.Add(instantiatedMethod);
460                         }
461                     }
462                     methodCandidates = methods.ToArray();
463                 }
464                 else
465                 {
466                     // Accept only candidates which are already instantiated
467                     methodCandidates = match.Where(m => m.ContainsGenericParameters == false).ToArray();
468                 }
469
470                 if (methodCandidates.Length == 0)
471                 {
472                     return null;
473                 }
474
475                 // Methods declared on this.declaringType class get top priority as matches
476                 Type declaringType = this.declaringType;
477                 MethodBase result = null;
478                 do
479                 {
480                     MethodBase[] methodsDeclaredHere = methodCandidates.Where(mb => mb.DeclaringType == declaringType).ToArray();
481                     if (methodsDeclaredHere.Length > 0)
482                     {
483                         // Try to find a match
484                         result = FindMatch(methodsDeclaredHere, bindingAttr, types, modifiers);
485                     }
486                     declaringType = declaringType.BaseType;
487                 }
488                 while (declaringType != null && result == null); // short-circuit as soon as we find a match
489
490                 return result; // returns null if no match found                
491             }
492
493             MethodBase FindMatch(MethodBase[] methodCandidates, BindingFlags bindingAttr, Type[] types, ParameterModifier[] modifiers)
494             {
495                 // Try the default binder first. Never gives false positive, but will fail to detect methods w/ parameter array because
496                 // it will not expand the formal parameter list when checking against actual parameters.
497                 MethodBase result = Type.DefaultBinder.SelectMethod(bindingAttr, methodCandidates, types, modifiers);
498
499                 // Could be false negative, check for parameter array and if so condense it back to an array before re-checking.
500                 if (result == null)
501                 {
502                     foreach (MethodBase method in methodCandidates)
503                     {
504                         MethodInfo methodInfo = method as MethodInfo;
505                         ParameterInfo[] formalParams = methodInfo.GetParameters();
506                         if (MethodResolver.HaveParameterArray(formalParams)) // Check if the last parameter of method is marked w/ "params" attribute
507                         {
508                             Type elementType = formalParams[formalParams.Length - 1].ParameterType.GetElementType();
509
510                             bool allCompatible = true;
511                             // There could be more actual parameters than formal parameters, because the formal parameter is a params T'[] for some T'.
512                             // So, check that each actual parameter starting at position [formalParams.Length - 1] is compatible with T'.
513                             for (int i = formalParams.Length - 1; i < types.Length - 1; i++)
514                             {
515                                 if (!TypeHelper.AreTypesCompatible(types[i], elementType))
516                                 {
517                                     allCompatible = false;
518                                     break;
519                                 }
520                             }
521
522                             if (!allCompatible)
523                             {
524                                 continue;
525                             }
526
527                             // Condense the actual parameter back to an array.
528                             Type[] typeArray = new Type[formalParams.Length];
529                             for (int i = 0; i < typeArray.Length - 1; i++)
530                             {
531                                 typeArray[i] = types[i];
532                             }
533                             typeArray[typeArray.Length - 1] = elementType.MakeArrayType();
534
535                             // Recheck the condensed array
536                             MethodBase newFound = Type.DefaultBinder.SelectMethod(bindingAttr, new MethodBase[] { methodInfo }, typeArray, modifiers);
537                             if (result != null && newFound != null)
538                             {
539                                 string type = newFound.ReflectedType.Name;
540                                 string name = newFound.Name;
541                                 string bindingType = bindingAttr == staticBindingFlags ? staticString : instanceString;
542                                 throw FxTrace.Exception.AsError(new AmbiguousMatchException(SR.DuplicateMethodFound(type, bindingType, name, this.parentActivity.DisplayName)));
543                             }
544                             else
545                             {
546                                 result = newFound;
547                             }
548                         }
549                     }
550                 }
551                 return result;
552             }
553
554             public override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers)
555             {
556                 throw FxTrace.Exception.AsError(new NotImplementedException());
557             }
558         }
559
560         // Executes method synchronously
561         class SyncMethodExecutor : MethodExecutor
562         {
563             MethodInfo syncMethod;
564             Func<object, object[], object> func;
565
566             public SyncMethodExecutor(CodeActivityMetadata metadata, MethodInfo syncMethod, Activity invokingActivity,
567                 Type targetType, InArgument targetObject, Collection<Argument> parameters,
568                 RuntimeArgument returnObject,
569                  MruCache<MethodInfo, Func<object, object[], object>> funcCache,
570                 ReaderWriterLockSlim locker)
571                 : base(invokingActivity, targetType, targetObject, parameters, returnObject)
572             {
573                 Fx.Assert(syncMethod != null, "Must provide syncMethod");
574                 this.syncMethod = syncMethod;
575                 this.func = MethodCallExpressionHelper.GetFunc(metadata, this.syncMethod, funcCache, locker);
576             }
577
578             public SyncMethodExecutor(SyncMethodExecutor copy, Type targetType, InArgument targetObject, Collection<Argument> parameters,
579                 RuntimeArgument returnObject)
580                 : base(copy.invokingActivity, targetType, targetObject, parameters, returnObject)
581             {
582                 this.syncMethod = copy.syncMethod;
583                 this.func = copy.func;
584             }
585
586             public bool IsTheSame(MethodInfo newMethod)
587             {
588                 return !MethodCallExpressionHelper.NeedRetrieve(newMethod, this.syncMethod, this.func);
589             }
590
591             public override bool MethodIsStatic { get { return this.syncMethod.IsStatic; } }
592
593             protected override IAsyncResult BeginMakeMethodCall(AsyncCodeActivityContext context, object target, AsyncCallback callback, object state)
594             {
595                 object[] actualParameters = EvaluateAndPackParameters(context, this.syncMethod, false);
596
597                 object result = this.InvokeAndUnwrapExceptions(this.func, target, actualParameters);
598
599                 SetOutArgumentAndReturnValue(context, result, actualParameters);
600
601                 return new CompletedAsyncResult(callback, state);
602             }
603
604             protected override void EndMakeMethodCall(AsyncCodeActivityContext context, IAsyncResult result)
605             {
606                 CompletedAsyncResult.End(result);
607             }
608         }
609
610         // Executes method using paired Begin/End async pattern methods
611         class AsyncPatternMethodExecutor : MethodExecutor
612         {
613
614             MethodInfo beginMethod;
615             MethodInfo endMethod;
616             Func<object, object[], object> beginFunc;
617             Func<object, object[], object> endFunc;
618             
619             public AsyncPatternMethodExecutor(CodeActivityMetadata metadata, MethodInfo beginMethod, MethodInfo endMethod,
620                 Activity invokingActivity, Type targetType, InArgument targetObject,
621                 Collection<Argument> parameters, RuntimeArgument returnObject,
622                  MruCache<MethodInfo, Func<object, object[], object>> funcCache,
623                 ReaderWriterLockSlim locker)
624                 : base(invokingActivity, targetType, targetObject, parameters, returnObject)
625             {
626                 Fx.Assert(beginMethod != null && endMethod != null, "Must provide beginMethod and endMethod");
627                 this.beginMethod = beginMethod;
628                 this.endMethod = endMethod;
629                 this.beginFunc = MethodCallExpressionHelper.GetFunc(metadata, beginMethod, funcCache, locker);
630                 this.endFunc = MethodCallExpressionHelper.GetFunc(metadata, endMethod, funcCache, locker);
631             }
632
633             public AsyncPatternMethodExecutor(AsyncPatternMethodExecutor copy, Type targetType, InArgument targetObject,
634                 Collection<Argument> parameters, RuntimeArgument returnObject)
635                 : base(copy.invokingActivity, targetType, targetObject, parameters, returnObject)
636             {
637                 this.beginMethod = copy.beginMethod;
638                 this.endMethod = copy.endMethod;
639                 this.beginFunc = copy.beginFunc;
640                 this.endFunc = copy.endFunc;
641             }
642
643             public override bool MethodIsStatic { get { return this.beginMethod.IsStatic; } }
644
645             public bool IsTheSame(MethodInfo newBeginMethod, MethodInfo newEndMethod)
646             {
647                 return !(MethodCallExpressionHelper.NeedRetrieve(newBeginMethod, this.beginMethod, this.beginFunc)
648                         || MethodCallExpressionHelper.NeedRetrieve(newEndMethod, this.endMethod, this.endFunc));
649             }
650
651             protected override IAsyncResult BeginMakeMethodCall(AsyncCodeActivityContext context, object target, AsyncCallback callback, object state)
652             {
653                 InvokeMethodInstanceData instance = new InvokeMethodInstanceData
654                 {
655                     TargetObject = target,
656                     ActualParameters = EvaluateAndPackParameters(context, this.beginMethod, true),
657                 };
658
659                 int count = instance.ActualParameters.Length;
660
661                 instance.ActualParameters[count - 2] = callback;
662                 instance.ActualParameters[count - 1] = state;
663                 context.UserState = instance;
664
665                 return (IAsyncResult)this.InvokeAndUnwrapExceptions(this.beginFunc, target, instance.ActualParameters);
666             }
667
668             protected override void EndMakeMethodCall(AsyncCodeActivityContext context, IAsyncResult result)
669             {
670                 InvokeMethodInstanceData instance = (InvokeMethodInstanceData)context.UserState;
671                 instance.ReturnValue = InvokeAndUnwrapExceptions(this.endFunc, instance.TargetObject, new object[] { result });
672                 this.SetOutArgumentAndReturnValue(context, instance.ReturnValue, instance.ActualParameters);
673             }
674         }
675
676         // Executes method asynchronously on WaitCallback thread.
677         class AsyncWaitCallbackMethodExecutor : MethodExecutor
678         {
679             MethodInfo asyncMethod;
680             Func<object, object[], object> asyncFunc;
681             
682             public AsyncWaitCallbackMethodExecutor(CodeActivityMetadata metadata, MethodInfo asyncMethod, Activity invokingActivity,
683                 Type targetType, InArgument targetObject, Collection<Argument> parameters,
684                 RuntimeArgument returnObject,
685                  MruCache<MethodInfo, Func<object, object[], object>> funcCache,
686                 ReaderWriterLockSlim locker)
687                 : base(invokingActivity, targetType, targetObject, parameters, returnObject)
688             {
689                 Fx.Assert(asyncMethod != null, "Must provide asyncMethod");
690                 this.asyncMethod = asyncMethod;
691                 this.asyncFunc = MethodCallExpressionHelper.GetFunc(metadata, asyncMethod, funcCache, locker);
692             }
693
694
695             public AsyncWaitCallbackMethodExecutor(AsyncWaitCallbackMethodExecutor copy, Type targetType, InArgument targetObject, 
696                 Collection<Argument> parameters, RuntimeArgument returnObject) : 
697                 base(copy.invokingActivity, targetType, targetObject, parameters, returnObject)
698             {
699                 this.asyncMethod = copy.asyncMethod;
700                 this.asyncFunc = copy.asyncFunc;
701             }
702
703             public override bool MethodIsStatic { get { return this.asyncMethod.IsStatic; } }
704
705             public bool IsTheSame(MethodInfo newMethodInfo)
706             {
707                 return !MethodCallExpressionHelper.NeedRetrieve(newMethodInfo, this.asyncMethod, this.asyncFunc);
708             }
709
710             protected override IAsyncResult BeginMakeMethodCall(AsyncCodeActivityContext context, object target, AsyncCallback callback, object state)
711             {
712                 InvokeMethodInstanceData instance = new InvokeMethodInstanceData
713                 {
714                     TargetObject = target,
715                     ActualParameters = EvaluateAndPackParameters(context, this.asyncMethod, false),
716                 };
717                 return new ExecuteAsyncResult(instance, this, callback, state);
718             }
719
720             protected override void EndMakeMethodCall(AsyncCodeActivityContext context, IAsyncResult result)
721             {
722                 InvokeMethodInstanceData instance = ExecuteAsyncResult.End(result);
723                 if (instance.ExceptionWasThrown)
724                 {
725                     throw FxTrace.Exception.AsError(instance.Exception);
726                 }
727                 else
728                 {
729                     this.SetOutArgumentAndReturnValue(context, instance.ReturnValue, instance.ActualParameters);
730                 }
731             }
732
733             class ExecuteAsyncResult : AsyncResult
734             {
735                 static Action<object> asyncExecute = new Action<object>(AsyncExecute);
736                 InvokeMethodInstanceData instance;
737                 AsyncWaitCallbackMethodExecutor executor;
738
739                 public ExecuteAsyncResult(InvokeMethodInstanceData instance, AsyncWaitCallbackMethodExecutor executor, AsyncCallback callback, object state)
740                     : base(callback, state)
741                 {
742                     this.instance = instance;
743                     this.executor = executor;
744                     ActionItem.Schedule(asyncExecute, this);
745                 }
746
747                 public static InvokeMethodInstanceData End(IAsyncResult result)
748                 {
749                     ExecuteAsyncResult thisPtr = AsyncResult.End<ExecuteAsyncResult>(result);
750                     return thisPtr.instance;
751                 }
752
753                 static void AsyncExecute(object state)
754                 {
755                     ExecuteAsyncResult thisPtr = (ExecuteAsyncResult)state;
756                     thisPtr.AsyncExecuteCore();
757                 }
758
759                 void AsyncExecuteCore()
760                 {
761                     try
762                     {
763                         this.instance.ReturnValue = this.executor.InvokeAndUnwrapExceptions(this.executor.asyncFunc, this.instance.TargetObject, this.instance.ActualParameters);
764                     }
765                     catch (Exception e)
766                     {
767                         if (Fx.IsFatal(e))
768                         {
769                             throw;
770                         }
771                         this.instance.Exception = e;
772                         this.instance.ExceptionWasThrown = true;
773                     }
774                     base.Complete(false);
775                 }
776             }
777         }
778     }
779 }