/// <summary> /// Take the topmost arguments down to the ARG_MARKER_STRING, pop them off, and then /// put them back again in reversed order so a function can read them in normal order. /// Note that if this is an indirect call, it will also consume the thing just under /// the ARG_MARKER, since that's expected to be the delegate or KOSDelegate that we already /// read and pulled the needed information from. /// <param name="cpu">the cpu we are running on, fur stack manipulation purposes</param> /// <param name="direct">need to know if this was a direct or indirect call. If indirect, /// then that means it also needs to consume the indirect reference off the stack just under /// the args</param> /// </summary> public static void ReverseStackArgs(ICpu cpu, bool direct) { List <object> args = new List <object>(); object arg = cpu.PopValueArgument(); while (cpu.GetArgumentStackSize() > 0 && arg.GetType() != ArgMarkerType) { args.Add(arg); // It's important to dereference with PopValue, not using PopArgumentStack, because the function // being called might not even be able to see the variable in scope anyway. // In other words, if calling a function like so: // declare foo to 3. // myfunc(foo). // The code inside myfunc needs to see that as being identical to just saying: // myfunc(3). // It has to be unaware of the fact that the name of the argument was 'foo'. It just needs to // see the contents that were inside foo. arg = cpu.PopValueArgument(); } if (!direct) { cpu.PopArgumentStack(); // throw away the delegate or KOSDelegate info - we already snarfed it by now. } // Push the arg marker back on again. cpu.PushArgumentStack(new KOSArgMarkerType()); // Push the arguments back on again, which will invert their order: foreach (object item in args) { cpu.PushArgumentStack(item); } }
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); } }