Beispiel #1
0
        /// <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);
            }
        }