private static object Invoke(PSInvokeMemberBinder binder, object o, int numArgs) { #if BINDERS_RUNTIME_STATS Stats.Increment(StatsCounter.InvokeMemberBinderInvoked); #endif // We can't cache the delegate related to property (has it could have been changed since last call) // We could workaround this limitation if we did have version number in the dynamic object (and detect if it changed - or changed function - since last call) var dc = o as IDynamicClass; if (dc != null) { var func = dc.__GetDynamicValue(binder.name) as Delegate; if (func != null) { // Use function, but let's compare with previous invoker if we can reuse it Delegate previousDelegate = binder.mPreviousDelegate; if ((previousDelegate == null) || (func.Target != previousDelegate.Target) || (func.Method != previousDelegate.Method)) { binder.mPreviousDelegate = func; binder.mInvoker = ActionCreator.CreateInvoker(func); // This is going to use an invoker factory (can be registered by user too for more optimal code). } return(binder.mInvoker.SafeInvokeWith(binder.mArgs)); // Now invoke, there is no parameters here } } if (o == binder.mPreviousTarget) { // If the object is the same, we directly invoke (no conversion needed in this case) return(binder.mInvoker.SafeInvokeWith(binder.mArgs)); } // Otherwise we have to find the corresponding method object result = binder.ResolveAndInvoke(o, numArgs); if (binder.mDelegate != null) { binder.mInvoker = ActionCreator.CreateInvoker(binder.mDelegate); } else { binder.mInvoker = ActionCreator.CreateInvoker(o, binder.mMethod.Method); } binder.mPreviousTarget = o; return(result); }
/// <summary> /// Invokes the method on o (potentially using a previous invoker as a hint). /// /// Note that this method works if the parameters haven been boxed, return value is also boxed. /// </summary> /// <param name="invoker">The previous invoker as a hint, null if we could not get the invoker during the fast path.</param> /// <param name="o">The target of the invocation.</param> private object ResolveAndInvoke(object o, bool invokeOnly) { // Property and same target, or same type has already been checked earlier (by the fast path) // So here we have a new target type, we have an overloading, or this is the first time // Parameters have been boxed and stored in mArgs already. // Note that if we are overloading, we still do a full CreateDelegate work, even if we had the same type, target and method... // This is slow, however it should pretty much never happen in game code. if ((mInvoker == null) && (invokeOnly == false)) { // It means that we did not get the invoker from the fast path (overloaded - rare - or it is the first time - very common). var dc = o as IDynamicClass; if (dc != null) { object value; if (dc.__TryGetDynamicValue(mName, out value) && value is Delegate) { var func = value as Delegate; // Assume that most time, it is due to the first execution, so don't compare with previous version mInvoker = ActionCreator.CreateInvoker(func); // This is going to use an invoker factory (can be registered by user too for more optimal code). invokeOnly = true; } } } if (invokeOnly) { Stats.Increment(StatsCounter.InvokeMemberBinderInvoked_Slow); return(mInvoker.SafeInvokeWith(mArgs)); } // If we reached this point, we have to do a new resolve, then invoke // determine object type Type otype; bool isStatic; if (o is Type) { // this is a static method invocation where o is the class otype = (Type)o; isStatic = true; } else { // this is a instance method invocation otype = o.GetType(); isStatic = false; } // see if type has changed if (otype != mType) { // re-resolve method list if type has changed mType = otype; // get method list for type and method name BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public; if (isStatic) { flags |= BindingFlags.Static; } else { flags |= BindingFlags.Instance; } mMethodList = MethodBinder.LookupMethodList(mType, mName, flags, mArgs.Length); // select new method to use, this will try to reuse mInvoker = SelectMethod(o); } else if (mOverloadState == OverloadState.HasOverload) { // if there are overloads we select the method every time // we could look into a more optimal way of doing this if it becomes a problem mInvoker = SelectMethod(o); } else { // Same instance type, no overload, so should be the same method (or none). // We might be able to update the invoker if only the target changed mInvoker = InvokerBase.UpdateOrCreate(mInvoker, o, mMethod.Method); } Stats.Increment(StatsCounter.InvokeMemberBinderInvoked_Slow); TypeLogger.LogType(o); return(mInvoker.SafeInvokeWith(mArgs)); }