private DelegateInfo DelegateInfo() { if (delegateInfo != null) { return(delegateInfo); } Delegate del = Delegate; MethodInfo methInfo = del.Method; ParameterInfo[] paramInfos = methInfo.GetParameters(); DelegateParameter[] delParams = new DelegateParameter[paramInfos.Length]; for (int i = 0; i < paramInfos.Length; i++) { delParams[i] = new DelegateParameter() { ParameterType = paramInfos[i].ParameterType, IsParams = (i == paramInfos.Length - 1) ? Attribute.IsDefined(paramInfos[i], typeof(ParamArrayAttribute)) : false }; } delegateInfo = new DelegateInfo() { Name = methInfo.Name, ReturnType = methInfo.ReturnType, Parameters = delParams }; return(delegateInfo); }
public void Invoke(ICpu cpu) { var args = new List <object>(); var paramArrayArgs = new List <Structure>(); // Will be true iff the lastmost parameter of the delegate is using the C# 'param' keyword and thus // expects the remainder of the arguments marshalled together into one array object. bool isParamArrayArg = false; CpuUtility.ReverseStackArgs(cpu, false); for (int i = 0; i < delInfo.Parameters.Length; ++i) { DelegateParameter paramInfo = delInfo.Parameters[i]; object arg = cpu.PopValueArgument(); Type argType = arg.GetType(); isParamArrayArg = i == delInfo.Parameters.Length - 1 && delInfo.Parameters[i].IsParams; if (arg != null && arg.GetType() == CpuUtility.ArgMarkerType) { if (isParamArrayArg) { break; // with param arguments, you want to consume everything to the arg bottom - it's normal. } else { throw new KOSArgumentMismatchException(delInfo.Parameters.Length, delInfo.Parameters.Length - (i + 1)); } } // Either the expected type of this one parameter, or if it's a 'param' array as the last arg, then // the expected type of that array's elements: Type paramType = (paramInfo.IsParams ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType); // Parameter type-safe checking: bool inheritable = paramType.IsAssignableFrom(argType); if (!inheritable) { bool castError = false; // If it's not directly assignable to the expected type, maybe it's "castable" to it: try { arg = Convert.ChangeType(arg, Type.GetTypeCode(paramType)); } catch (InvalidCastException) { throw new KOSCastException(argType, paramType); } catch (FormatException) { castError = true; } if (castError) { throw new Exception(string.Format("Argument {0}({1}) to method {2} should be {3} instead of {4}.", (delInfo.Parameters.Length - i), arg, delInfo.Name, paramType.Name, argType)); } } if (isParamArrayArg) { paramArrayArgs.Add(Structure.FromPrimitiveWithAssert(arg)); --i; // keep hitting the last item in the param list again and again until a forced break because of arg bottom marker. } else { args.Add(Structure.FromPrimitiveWithAssert(arg)); } } if (isParamArrayArg) { // collect the param array args that were at the end into the one single // array item that will be sent to the method when invoked: args.Add(paramArrayArgs.ToArray()); } // Consume the bottom marker under the args, which had better be // immediately under the args we just popped, or the count was off. if (!isParamArrayArg) // A param array arg will have already consumed the arg bottom mark. { bool foundArgMarker = false; int numExtraArgs = 0; while (cpu.GetArgumentStackSize() > 0 && !foundArgMarker) { object marker = cpu.PopValueArgument(); if (marker != null && marker.GetType() == CpuUtility.ArgMarkerType) { foundArgMarker = true; } else { ++numExtraArgs; } } if (numExtraArgs > 0) { throw new KOSArgumentMismatchException(delInfo.Parameters.Length, delInfo.Parameters.Length + numExtraArgs); } } // Delegate.DynamicInvoke expects a null, rather than an array of zero length, when // there are no arguments to pass: object[] argArray = (args.Count > 0) ? args.ToArray() : null; object val = call(argArray); if (delInfo.ReturnType == typeof(void)) { value = ScalarValue.Create(0); } else { value = Structure.FromPrimitiveWithAssert(val); } }