PageRenderTime 33ms CodeModel.GetById 19ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 1ms

/Microsoft.Scripting/Utils/ReflectedCaller.cs

https://bitbucket.org/stefanrusek/xronos
C# | 197 lines | 137 code | 26 blank | 34 comment | 28 complexity | 31ab5cff6db38b6b0f9b47add08645f3 MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Microsoft Public License. A 
  6 * copy of the license can be found in the License.html file at the root of this distribution. If 
  7 * you cannot locate the  Microsoft Public License, please send an email to 
  8 * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
  9 * by the terms of the Microsoft Public License.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15
 16#if CODEPLEX_40
 17using System;
 18#else
 19using System; using Microsoft;
 20#endif
 21using System.Collections.Generic;
 22using System.Reflection;
 23using System.Reflection.Emit;
 24using Microsoft.Scripting.Runtime;
 25
 26namespace Microsoft.Scripting.Utils {
 27    public partial class ReflectedCaller {
 28        internal ReflectedCaller() { }
 29        private static readonly Dictionary<MethodInfo, ReflectedCaller> _cache = new Dictionary<MethodInfo,ReflectedCaller>();
 30
 31        /// <summary>
 32        /// Creates a new ReflectedCaller which can be used to quickly invoke the provided MethodInfo.
 33        /// </summary>
 34        public static ReflectedCaller Create(MethodInfo info) {
 35            if ((!info.IsStatic && info.DeclaringType.IsValueType) || 
 36                info is System.Reflection.Emit.DynamicMethod) {
 37                return new SlowReflectedCaller(info);
 38            }            
 39
 40            ParameterInfo[] pis = info.GetParameters();
 41            int argCnt = pis.Length;
 42            if (!info.IsStatic) argCnt++;
 43            if (argCnt >= MaxHelpers) {
 44                // no delegate for this size, fallback to reflection invoke
 45                return new SlowReflectedCaller(info);
 46            }
 47
 48            foreach (ParameterInfo pi in pis) {
 49                if (pi.ParameterType.IsByRef) {
 50                    // we don't support ref args via generics.
 51                    return new SlowReflectedCaller(info);
 52                }
 53            }
 54
 55            // see if we've created one w/ a delegate
 56            ReflectedCaller res;
 57            if (ShouldCache(info)) {
 58                lock (_cache) {
 59                    if (_cache.TryGetValue(info, out res)) {
 60                        return res;
 61                    }
 62                }
 63            }
 64
 65            // create it 
 66            try {
 67                if (argCnt < MaxArgs) {
 68                    res = FastCreate(info, pis);
 69                } else {
 70                    res = SlowCreate(info, pis);
 71                }
 72            } catch (TargetInvocationException tie) {
 73                if (!(tie.InnerException is NotSupportedException)) {
 74                    throw;
 75                }
 76
 77                res = new SlowReflectedCaller(info);
 78            } catch (NotSupportedException) {
 79                // if Delegate.CreateDelegate can't handle the method fallback to 
 80                // the slow reflection version.  For example this can happen w/ 
 81                // a generic method defined on an interface and implemented on a class.
 82                res = new SlowReflectedCaller(info);
 83            }
 84
 85            // cache it for future users if it's a reasonable method to cache
 86            if (ShouldCache(info)) {
 87                lock (_cache) {
 88                    _cache[info] = res;
 89                }
 90            }
 91
 92            return res;            
 93        }
 94
 95        private static bool ShouldCache(MethodInfo info) {            
 96            return !(info is DynamicMethod);
 97        }
 98               
 99        /// <summary>
100        /// Gets the next type or null if no more types are available.
101        /// </summary>
102        private static Type TryGetParameterOrReturnType(MethodInfo target, ParameterInfo[] pi, int index) {
103            if (!target.IsStatic) {
104                index--;
105                if (index < 0) {
106                    return target.DeclaringType;
107                }
108            }
109
110            if (index < pi.Length) {
111                // next in signature
112                return pi[index].ParameterType;
113            }
114
115            if (target.ReturnType == typeof(void) || index > pi.Length) {
116                // no more parameters
117                return null;
118            }
119
120            // last parameter on Invoke is return type
121            return target.ReturnType;
122        }
123
124        private static bool IndexIsNotReturnType(int index, MethodInfo target, ParameterInfo[] pi) {
125            return pi.Length != index || (pi.Length == index && !target.IsStatic);
126        }
127
128        /// <summary>
129        /// Uses reflection to create new instance of the appropriate ReflectedCaller
130        /// </summary>
131        private static ReflectedCaller SlowCreate(MethodInfo info, ParameterInfo[] pis) {
132            List<Type> types = new List<Type>();
133            if (!info.IsStatic) types.Add(info.DeclaringType);
134            foreach (ParameterInfo pi in pis) {
135                types.Add(pi.ParameterType);
136            }
137            if (info.ReturnType != typeof(void)) {
138                types.Add(info.ReturnType);
139            }
140            Type[] arrTypes = types.ToArray();
141
142            return (ReflectedCaller)Activator.CreateInstance(GetHelperType(info, arrTypes), info);
143        }
144    }
145
146    sealed partial class SlowReflectedCaller : ReflectedCaller {
147        private MethodInfo _target;
148
149        public SlowReflectedCaller(MethodInfo target) {
150            _target = target;
151        }
152        
153        public override object Invoke(params object[] args) {
154            return InvokeWorker(args);
155        }        
156       
157        public override object InvokeInstance(object instance, params object[] args) {
158            if (_target.IsStatic) {
159                try {
160                    return _target.Invoke(null, args);
161                } catch (TargetInvocationException e) {
162                    throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
163                }
164            }
165
166            try {
167                return _target.Invoke(instance, args);
168            } catch (TargetInvocationException e) {
169                throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
170            }
171        }
172
173        private object InvokeWorker(params object[] args) {
174            if (_target.IsStatic) {
175                try {
176                    return _target.Invoke(null, args);
177                } catch (TargetInvocationException e) {
178                    throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
179                }
180            }
181
182            try {
183                return _target.Invoke(args[0], GetNonStaticArgs(args));
184            } catch (TargetInvocationException e) {
185                throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
186            }
187        }
188
189        private static object[] GetNonStaticArgs(object[] args) {
190            object[] newArgs = new object[args.Length - 1];
191            for (int i = 0; i < newArgs.Length; i++) {
192                newArgs[i] = args[i + 1];
193            }
194            return newArgs;
195        }
196    }
197}