Пример #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.PopValue();
            while (cpu.GetStackSize() > 0 && arg.GetType() != ArgMarkerType)
            {
                args.Add(arg);

                // It's important to dereference with PopValue, not using PopStack, 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.PopValue();
            }
            if (! direct)
                cpu.PopStack(); // throw away the delegate or KOSDelegate info - we already snarfed it by now.
            // Push the arg marker back on again.
            cpu.PushStack(new KOSArgMarkerType());
            // Push the arguments back on again, which will invert their order:
            foreach (object item in args)
                cpu.PushStack(item);
        }
Пример #2
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.PopValue();

            while (cpu.GetStackSize() > 0 && arg.GetType() != ArgMarkerType)
            {
                args.Add(arg);

                // It's important to dereference with PopValue, not using PopStack, 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.PopValue();
            }
            if (!direct)
            {
                cpu.PopStack(); // throw away the delegate or KOSDelegate info - we already snarfed it by now.
            }
            // Push the arg marker back on again.
            cpu.PushStack(new KOSArgMarkerType());
            // Push the arguments back on again, which will invert their order:
            foreach (object item in args)
            {
                cpu.PushStack(item);
            }
        }
Пример #3
0
        /// <summary>
        /// Performs the actual execution of a subroutine call, either from this opcode or externally from elsewhere.
        /// All "call a routine" logic should shunt through this code here, which handles all the complex cases,
        /// or at least it should.
        /// Note that in the case of a user function, this does not *ACTUALLY* execute the function yet.  It just
        /// arranges the stack correctly for the call and returns the new location that the IP should be jumped to
        /// on the next instruction to begin the subroutine.  For all built-in cases, it actually executes the 
        /// call right now and doesn't return until it's done.  But for User functions it can't do that - it can only
        /// advise on where to jump on the next instruction to begin the function.
        /// </summary>
        /// <param name="cpu">the cpu its running on</param>
        /// <param name="direct">same meaning as OpcodeCall.Direct</param>
        /// <param name="destination">if direct, then this is the function name</param>
        /// <param name="calledFromKOSDelegateCall">true if KOSDelegate.Call() brought us here.  If true that
        /// means any pre-bound args are already on the stack.  If false it means they aren't and this will have to
        /// put them there.</param>
        /// <returns>new IP to jump to, if this should be followed up by a jump.  If -1 then it means don't jump.</returns>
        public static int StaticExecute(ICpu cpu, bool direct, object destination, bool calledFromKOSDelegateCall)
        {
            object functionPointer;
            object delegateReturn = null;
            int newIP = -1; // new instruction pointer to jump to, next, if any.

            if (direct)
            {
                functionPointer = cpu.GetValue(destination);
                if (functionPointer == null)
                    throw new KOSException("Attempt to call function failed - Value of function pointer for " + destination + " is null.");
            }
            else // for indirect calls, dig down to find what's underneath the argument list in the stack and use that:
            {
                bool foundBottom = false;
                int digDepth;
                int argsCount = 0;
                for (digDepth = 0; (! foundBottom) && digDepth < cpu.GetStackSize() ; ++digDepth)
                {
                    object arg = cpu.PeekValue(digDepth);
                    if (arg != null && arg.GetType() == ArgMarkerType)
                        foundBottom = true;
                    else
                        ++argsCount;
                }
                functionPointer = cpu.PeekValue(digDepth);
                if (! ( functionPointer is Delegate || functionPointer is KOSDelegate || functionPointer is ISuffixResult))
                {
                    // Indirect calls are meant to be delegates.  If they are not, then that means the
                    // function parentheses were put on by the user when they weren't required.  Just dig
                    // through the stack to the result of the getMember and skip the rest of the execute logic

                    // If args were passed to a non-method, then clean them off the stack, and complain:
                    if (argsCount>0)
                    {
                        for (int i=1 ; i<=argsCount; ++i)
                            cpu.PopValue();
                        throw new KOSArgumentMismatchException(
                            0, argsCount, "\n(In fact in this case the parentheses are entirely optional)");
                    }
                    cpu.PopValue(); // pop the ArgMarkerString too.
                    return -1;
                }
            }

            // If it's a string it might not really be a built-in, it might still be a user func.
            // Detect whether it's built-in, and if it's not, then convert it into the equivalent
            // user func call by making it be an integer instruction pointer instead:
            if (functionPointer is string || functionPointer is StringValue)
            {
                string functionName = functionPointer.ToString();
                if (functionName.EndsWith("()"))
                    functionName = functionName.Substring(0, functionName.Length - 2);
                if (!(cpu.BuiltInExists(functionName)))
                {
                    // It is not a built-in, so instead get its value as a user function pointer variable, despite
                    // the fact that it's being called AS IF it was direct.
                    if (!functionName.EndsWith("*")) functionName = functionName + "*";
                    if (!functionName.StartsWith("$")) functionName = "$" + functionName;
                    functionPointer = cpu.GetValue(functionName);
                }
            }

            KOSDelegate kosDelegate = functionPointer as KOSDelegate;
            if (kosDelegate != null)
            {
                if (! calledFromKOSDelegateCall)
                    kosDelegate.InsertPreBoundArgs();
            }

            IUserDelegate userDelegate = functionPointer as IUserDelegate;
            if (userDelegate != null)
                functionPointer = userDelegate.EntryPoint;

            BuiltinDelegate builtinDel = functionPointer as BuiltinDelegate;
            if (builtinDel != null && (! calledFromKOSDelegateCall) )
                functionPointer = builtinDel.Name;

            // If the IP for a jump location got encapsulated as a user int when it got stored
            // into the internal variable, then get the primitive int back out of it again:
            ScalarIntValue userInt = functionPointer as ScalarIntValue;
            if (userInt != null)
                functionPointer = userInt.GetIntValue();

            // Convert to int instead of cast in case the identifier is stored
            // as an encapsulated ScalarValue, preventing an unboxing collision.
            if (functionPointer is int || functionPointer is ScalarValue)
            {
                CpuUtility.ReverseStackArgs(cpu, direct);
                var contextRecord = new SubroutineContext(cpu.InstructionPointer+1);
                newIP = Convert.ToInt32(functionPointer);

                cpu.PushAboveStack(contextRecord);
                if (userDelegate != null)
                {
                    cpu.AssertValidDelegateCall(userDelegate);
                    // Reverse-push the closure's scope record, just after the function return context got put on the stack.
                    for (int i = userDelegate.Closure.Count - 1 ; i >= 0 ; --i)
                        cpu.PushAboveStack(userDelegate.Closure[i]);
                }
            }
            else if (functionPointer is string)
            {
                // Built-ins don't need to dereference the stack values because they
                // don't leave the scope - they're not implemented that way.  But later we
                // might want to change that.
                var name = functionPointer as string;
                string functionName = name;
                if (functionName.EndsWith("()"))
                    functionName = functionName.Substring(0, functionName.Length - 2);
                cpu.CallBuiltinFunction(functionName);

                // If this was indirect, we need to consume the thing under the return value.
                // as that was the indirect BuiltInDelegate:
                if ((! direct) && builtinDel != null)
                {
                    object topThing = cpu.PopStack();
                    cpu.PopStack(); // remove BuiltInDelegate object.
                    cpu.PushStack(topThing); // put return value back.
                }
            }
            else if (functionPointer is ISuffixResult)
            {
                var result = (ISuffixResult) functionPointer;

                if (!result.HasValue)
                {
                    result.Invoke(cpu);
                }

                delegateReturn = result.Value;
            }
            // TODO:erendrake This else if is likely never used anymore
            else if (functionPointer is Delegate)
            {
                throw new KOSYouShouldNeverSeeThisException("OpcodeCall unexpected function pointer delegate");
            }
            else
            {
                throw new KOSNotInvokableException(functionPointer);
            }

            if (functionPointer is ISuffixResult)
            {
                if (! (delegateReturn is KOSPassThruReturn))
                    cpu.PushStack(delegateReturn); // And now leave the return value on the stack to be read.
            }

            return newIP;
        }
Пример #4
0
 public override void Execute(ICpu cpu)
 {
     int functionPointer = Convert.ToInt32(cpu.PopValue()); // in case it got wrapped in a ScalarIntValue
     cpu.AddTrigger(functionPointer);
 }
Пример #5
0
 public override void Execute(ICpu cpu)
 {
     // Wait model is: Because waits can occur in triggers as well as in
     // mainline code, the CPU can't be the one to be tracking the
     // remaining time on the wait.  There can be more than one place where
     // a wait was pending.  So instead track the pending time inside the
     // opcode itself, by having the opcode store its timestamp when it is
     // meant to end, and checking it each time.
     if (endTime < 0) // initial time being executed.
     {
         double arg = Convert.ToDouble(cpu.PopValue());
         endTime = cpu.StartWait(arg);
         DeltaInstructionPointer = 0; // stay here next time.
     }
     else if (cpu.SessionTime < endTime) // It's in the midst of an already running wait, and it's not expired yet.
     {
         cpu.StartWait(0); // kick back to wait mode again.
         DeltaInstructionPointer = 0; // stay here next time, to test this again.
     }
     else // It was in an already running wait, and the wait just expired.
     {
         endTime = -1; // reset in case this is called again in a loop.
         DeltaInstructionPointer = 1; // okay now move on.
     }
 }
Пример #6
0
        public override void Execute(ICpu cpu)
        {
            // Even though this value is being thrown away it's still important to attempt to
            // process it (with cpu.PopValue()) rather than ignore it (with cpu.PopStack()).  This
            // is just in case it's an unknown variable name in need of an error message
            // to the user.  Detecting that a variable name is unknown occurs during the popping
            // of the value, not the pushing of it.  (This is necessary because SET and DECLARE
            // statements have to be allowed to push undefined variable references onto the stack
            // for new variables that they are going to create.)

            cpu.PopValue();
        }
Пример #7
0
 public override void Execute(ICpu cpu)
 {
     bool argument2 = Convert.ToBoolean(cpu.PopValue());
     bool argument1 = Convert.ToBoolean(cpu.PopValue());
     object result = argument1 || argument2;
     cpu.PushStack(Structure.FromPrimitive(result));
 }
Пример #8
0
        public override void Execute(ICpu cpu)
        {
            object right = cpu.PopValue();
            object left = cpu.PopValue();

            var operands = new OperandPair(left, right);

            Calculator calc = Calculator.GetCalculator(operands);
            Operands = operands;
            object result = ExecuteCalculation(calc);
            cpu.PushStack(result);
        }
Пример #9
0
 public override void Execute(ICpu cpu)
 {
     cpu.PushStack(cpu.PopValue());
 }
Пример #10
0
        /// <summary>
        /// Call this when executing a delegate function whose delegate object was stored on
        /// the stack underneath the arguments.  The code here is using reflection and complex
        /// enough that it needed to be separated from the main Execute method.
        /// </summary>
        /// <param name="cpu">the cpu this opcode is being called on</param>
        /// <param name="dlg">the delegate object this opcode is being called for.</param>
        /// <returns>whatever object the delegate method returned</returns>
        public static object ExecuteDelegate(ICpu cpu, Delegate dlg)
        {
            MethodInfo methInfo = dlg.Method;
            ParameterInfo[] paramArray = methInfo.GetParameters();
            var args = new List<object>();
            
            // Iterating over parameter signature backward because stack:
            for (int i = paramArray.Length - 1 ; i >= 0 ; --i)
            {
                object arg = cpu.PopValue();
                if (arg != null && arg.GetType() == ArgMarkerType)
                    throw new KOSArgumentMismatchException(paramArray.Length, paramArray.Length - (i+1));
                Type argType = arg.GetType();
                ParameterInfo paramInfo = paramArray[i];
                Type paramType = 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}.", (paramArray.Length - i), arg, methInfo.Name, paramType.Name, argType));
                    }
                }
                
                args.Add(arg);
            }
            // Consume the bottom marker under the args, which had better be
            // immediately under the args we just popped, or the count was off:
            bool foundArgMarker = false;
            int numExtraArgs = 0;
            while (cpu.GetStackSize() > 0 && !foundArgMarker)
            {
                object marker = cpu.PopValue();
                if (marker != null && marker.GetType() == ArgMarkerType)
                    foundArgMarker = true;
                else
                    ++numExtraArgs;
            }
            if (numExtraArgs > 0)
                throw new KOSArgumentMismatchException(paramArray.Length, paramArray.Length + numExtraArgs);

            args.Reverse(); // Put back in normal order instead of stack order.
            
            // Dialog.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;

            try
            {
                // I could find no documentation on what DynamicInvoke returns when the delegate
                // is a function returning void.  Does it return a null?  I don't know.  So to avoid the
                // problem, I split this into these two cases:
                if (methInfo.ReturnType == typeof(void))
                {
                    dlg.DynamicInvoke(argArray);
                    return null; // So that the compiler building the opcodes for a function call statement doesn't
                                 // have to know the function prototype to decide whether or
                                 // not it needs to pop a value from the stack for the return value.  By adding this,
                                 // it can unconditionally assume there will be exactly 1 value left behind on the stack
                                 // regardless of what function it was that was being called.
                }
                return dlg.DynamicInvoke(argArray);
            }
            catch (TargetInvocationException e)
            {
                // Annoyingly, calling DynamicInvoke on a delegate wraps any exceptions the delegate throws inside
                // this TargetInvocationException, which hides them from the kOS user unless we do this:
                if (e.InnerException != null)
                    throw e.InnerException;
                throw;
            }
        }
Пример #11
0
        public override void Execute(ICpu cpu)
        {
            object functionPointer;
            object delegateReturn = null;

            if (Direct)
            {
                functionPointer = cpu.GetValue(Destination);
                if (functionPointer == null)
                    throw new KOSException("Attempt to call function failed - Value of function pointer for " + Destination + " is null.");
            }
            else // for indirect calls, dig down to find what's underneath the argument list in the stack and use that:
            {
                bool foundBottom = false;
                int digDepth;
                int argsCount = 0;
                for (digDepth = 0; (! foundBottom) && digDepth < cpu.GetStackSize() ; ++digDepth)
                {
                    object arg = cpu.PeekValue(digDepth);
                    if (arg != null && arg.GetType() == ArgMarkerType)
                        foundBottom = true;
                    else
                        ++argsCount;
                }
                functionPointer = cpu.PeekValue(digDepth);
                if (! ( functionPointer is Delegate))
                {
                    // Indirect calls are meant to be delegates.  If they are not, then that means the
                    // function parentheses were put on by the user when they weren't required.  Just dig
                    // through the stack to the result of the getMember and skip the rest of the execute logic

                    // If args were passed to a non-method, then clean them off the stack, and complain:
                    if (argsCount>0)
                    {
                        for (int i=1 ; i<=argsCount; ++i)
                            cpu.PopValue();
                        throw new KOSArgumentMismatchException(
                            0, argsCount, "\n(In fact in this case the parentheses are entirely optional)");
                    }
                    cpu.PopValue(); // pop the ArgMarkerString too.
                    return;
                }
            }
            
            // If it's a string it might not really be a built-in, it might still be a user func.
            // Detect whether it's built-in, and if it's not, then convert it into the equivalent
            // user func call by making it be an integer instruction pointer instead:
            if (functionPointer is string)
            {
                string functionName = functionPointer as string;
                if (functionName.EndsWith("()"))
                    functionName = functionName.Substring(0, functionName.Length - 2);
                if (!(cpu.BuiltInExists(functionName)))
                {
                    // It is not a built-in, so instead get its value as a user function pointer variable, despite 
                    // the fact that it's being called AS IF it was direct.
                    if (!functionName.EndsWith("*")) functionName = functionName + "*";
                    if (!functionName.StartsWith("$")) functionName = "$" + functionName;
                    functionPointer = cpu.GetValue(functionName);
                }
            }
            IUserDelegate userDelegate = functionPointer as IUserDelegate;
            if (userDelegate != null)
                functionPointer = userDelegate.EntryPoint;

            if (functionPointer is int)
            {
                ReverseStackArgs(cpu);
                int currentPointer = cpu.InstructionPointer;
                DeltaInstructionPointer = (int)functionPointer - currentPointer;
                var contextRecord = new SubroutineContext(currentPointer+1);
                cpu.PushAboveStack(contextRecord);
                if (userDelegate != null)
                {
                    cpu.AssertValidDelegateCall(userDelegate);
                    // Reverse-push the closure's scope record, just after the function return context got put on the stack.
                    for (int i = userDelegate.Closure.Count - 1 ; i >= 0 ; --i)
                        cpu.PushAboveStack(userDelegate.Closure[i]);
                }
            }
            else if (functionPointer is string)
            {
                // Built-ins don't need to dereference the stack values because they
                // don't leave the scope - they're not implemented that way.  But later we
                // might want to change that.
                var name = functionPointer as string;
                string functionName = name;
                if (functionName.EndsWith("()"))
                    functionName = functionName.Substring(0, functionName.Length - 2);
                cpu.CallBuiltinFunction(functionName);
            }
            else if (functionPointer is Delegate)
            {
                delegateReturn = ExecuteDelegate(cpu, (Delegate)functionPointer);
            }
            else
            {
                // This is one of those "the user had better NEVER see this error" sorts of messages that's here to keep us in check:
                throw new Exception(
                    string.Format("kOS internal error: OpcodeCall calling a function described using {0} which is of type {1} and kOS doesn't know how to call that.", functionPointer, functionPointer.GetType().Name)
                    );
            }

            if (! Direct)
            {
                cpu.PopValue(); // consume function name, branch index, or delegate
            }
            if (functionPointer is Delegate)
            {
                cpu.PushStack(delegateReturn); // And now leave the return value on the stack to be read.
            }
        }
Пример #12
0
 public override void Execute(ICpu cpu)
 {
     bool argument2 = Convert.ToBoolean(cpu.PopValue());
     bool argument1 = Convert.ToBoolean(cpu.PopValue());
     object result = argument1 | argument2;
     cpu.PushStack(result);
 }
Пример #13
0
        public override void Execute(ICpu cpu)
        {
            object value = cpu.PopValue();
            object result;

            if (value is bool)
                result = !((bool)value);
            else if (value is int)
                result = Convert.ToBoolean(value) ? 0 : 1;
            else if ((value is double) || (value is float))
                result = Convert.ToBoolean(value) ? 0.0 : 1.0;
            else
                throw new KOSUnaryOperandTypeException("boolean-not", value);

            cpu.PushStack(result);
        }
Пример #14
0
 public override void Execute(ICpu cpu)
 {
     object value = cpu.PopValue();
     bool result = Convert.ToBoolean(value);
     cpu.PushStack(result);
 }
Пример #15
0
        public override void Execute(ICpu cpu)
        {
            object value = cpu.PopValue();
            object result;

            if (value is int)
                result = -((int)value);
            else if (value is float)
                result = -(Convert.ToDouble(value));
            else if (value is double)
                result = -((double)value);
            else
            {
                // Generic last-ditch to catch any sort of object that has
                // overloaded the unary negate operator '-'.
                // (For example, kOS.Suffixed.Vector and kOS.Suffixed.Direction)
                Type t = value.GetType();
                MethodInfo negateMe = t.GetMethod("op_UnaryNegation", BindingFlags.Static | BindingFlags.Public); // C#'s alternate name for '-' operator
                if (negateMe != null)
                    result = negateMe.Invoke(null, new[]{value}); // value is an arg, not the 'this'.  (Method is static.)
                else
                    throw new KOSUnaryOperandTypeException("negate", value);
            }

            cpu.PushStack(result);
        }
Пример #16
0
 public override void Execute(ICpu cpu)
 {
     object value = cpu.PopValue();
     var identifier = (string)cpu.PopStack();
     cpu.SetNewLocal(identifier, value);
 }
Пример #17
0
 public override void Execute(ICpu cpu)
 {
     double arg = Convert.ToDouble(cpu.PopValue());
     cpu.YieldProgram(new YieldFinishedGameTimer(arg));
 }
Пример #18
0
 public override void Execute(ICpu cpu)
 {
     var functionPointer = (int)cpu.PopValue();
     cpu.RemoveTrigger(functionPointer);
 }
Пример #19
0
        public void Invoke(ICpu cpu)
        {
            MethodInfo methInfo = del.Method;
            ParameterInfo[] paramArray = methInfo.GetParameters();
            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 < paramArray.Length; ++i)
            {
                object arg = cpu.PopValue();
                Type argType = arg.GetType();
                ParameterInfo paramInfo = paramArray[i];

                // If this is the lastmost parameter then it might be a 'param' array which expects all the rest of
                // the arguments to be collected together into one single array parameter when invoking the method:
                isParamArrayArg = (i == paramArray.Length - 1 && Attribute.IsDefined(paramInfo, typeof(ParamArrayAttribute)));

                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(paramArray.Length, paramArray.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 = (isParamArrayArg ? 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}.", (paramArray.Length - i), arg, methInfo.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.GetStackSize() > 0 && !foundArgMarker)
                {
                    object marker = cpu.PopValue();
                    if (marker != null && marker.GetType() == CpuUtility.ArgMarkerType)
                        foundArgMarker = true;
                    else
                        ++numExtraArgs;
                }
                if (numExtraArgs > 0)
                    throw new KOSArgumentMismatchException(paramArray.Length, paramArray.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;

            try
            {
                // I could find no documentation on what DynamicInvoke returns when the delegate
                // is a function returning void.  Does it return a null?  I don't know.  So to avoid the
                // problem, I split this into these two cases:
                if (methInfo.ReturnType == typeof(void))
                {
                    del.DynamicInvoke(argArray);
                    value = ScalarValue.Create(0);
                    // By adding this we can unconditionally assume all functionshave a return value
                    // to be used or popped away, even if "void".  In order to mainain consistency with
                    // the void return value of functions, and to ensure that we don't accidentally pass
                    // a value back to the user that they cannot interact with (null), we return zero.
                }
                else
                {
                    // Convert a primitive return type to a structure.  This is done in the opcode, since
                    // the opcode calls the deligate directly and cannot be (quickly) intercepted
                    value = Structure.FromPrimitiveWithAssert(del.DynamicInvoke(argArray));
                }
            }
            catch (TargetInvocationException e)
            {
                // Annoyingly, calling DynamicInvoke on a delegate wraps any exceptions the delegate throws inside
                // this TargetInvocationException, which hides them from the kOS user unless we do this:
                if (e.InnerException != null)
                    throw e.InnerException;
                throw;
            }
        }
Пример #20
0
 public override void Execute(ICpu cpu)
 {
     object arg = cpu.PopValue();
     cpu.StartWait(Convert.ToDouble(arg));
 }
Пример #21
0
        public override void Execute(ICpu cpu)
        {
            object value = cpu.PopValue();
            object result;

            // Convert to bool instead of cast in case the identifier is stored
            // as an encapsulated BooleanValue, preventing an unboxing collision.
            // Wrapped in a try/catch since the Convert framework doesn't have a
            // way to determine if a type can be converted.
            try
            {
                result = !Convert.ToBoolean(value);
            }
            catch
            {
                throw new KOSCastException(value.GetType(), typeof(BooleanValue));
            }
            cpu.PushStack(Structure.FromPrimitive(result));
        }
Пример #22
0
        public override void Execute(ICpu cpu)
        {
            Argument2 = cpu.PopValue();
            Argument1 = cpu.PopValue();

            // convert floats to doubles
            if (Argument1 is float) Argument1 = Convert.ToDouble(Argument1);
            if (Argument2 is float) Argument2 = Convert.ToDouble(Argument2);

            Calculator calc = Calculator.GetCalculator(Argument1, Argument2);
            object result = ExecuteCalculation(calc);
            cpu.PushStack(result);
        }
Пример #23
0
 public override void Execute(ICpu cpu)
 {
     // This may look like it's just pointlessly converting from a
     // ScalarBoolean to a primitive boolean and then back into a
     // ScalarBoolean, and in the case where the operand was already
     // a ScalarBoolean that would be true.  But the purpose of this opcode
     // is to also change integers and floats into booleans. Thus the call to
     // Convert.ToBoolean():
     object value = cpu.PopValue();
     bool result = Convert.ToBoolean(value);
     cpu.PushStack(Structure.FromPrimitive(result));
 }
Пример #24
0
        public override void Execute(ICpu cpu)
        {
            string suffixName = cpu.PopStack().ToString().ToUpper();
            object popValue = cpu.PopValue();

            var specialValue = popValue as ISuffixed;
            
            if (specialValue == null)
            {
                var s = popValue as string;
                if (s != null)
                {
                    specialValue = new StringValue(s);
                }
                else
                {
                    throw new Exception(string.Format("Values of type {0} cannot have suffixes", popValue.GetType()));
                }
            }

            object value = specialValue.GetSuffix(suffixName);
            if (value is Delegate && !IsMethodCallAttempt)
            {
                // This is what happens when someone tries to call a suffix method as if
                // it wasn't a method (i.e. leaving the parentheses off the call).  The
                // member returned is a delegate that needs to be called to get its actual
                // value.  Borrowing the same routine that OpcodeCall uses for its method calls:
                cpu.PushStack(new KOSArgMarkerType());
                value = OpcodeCall.ExecuteDelegate(cpu, (Delegate)value);
            }

            cpu.PushStack(value);
        }
Пример #25
0
        public override void Execute(ICpu cpu)
        {
            // Return value should be atop the stack.
            // Pop it, eval it, and push it back,
            // i.e. if the statement was RETURN X, and X is 2, then you want
            // it to return the number 2, not the variable name $x, which could
            // be a variable local to this function which is about to go out of scope
            // so the caller can't access it:
            object returnVal = cpu.PopValue();

            // Now dig down through the stack until the argbottom is found.
            // anything still leftover above that should be unread parameters we
            // should throw away:
            object shouldBeArgMarker = 0; // just a temp to force the loop to execute at least once.
            while (shouldBeArgMarker == null || (shouldBeArgMarker.GetType() != OpcodeCall.ArgMarkerType))
            {
                if (cpu.GetStackSize() <= 0)
                {
                    throw new KOSArgumentMismatchException(
                        string.Format("Something is wrong with the stack - no arg bottom mark when doing a return.  This is an internal problem with kOS")
                       );
                }
                shouldBeArgMarker = cpu.PopStack();
            }

            cpu.PushStack(Structure.FromPrimitive(returnVal));

            // Now, after the eval was done, NOW finally pop the scope, after we don't need local vars anymore:
            if( Depth > 0 )
                OpcodePopScope.DoPopScope(cpu, Depth);

            // The only thing on the "above stack" now that is allowed to get in the way of
            // finding the context record that tells us where to jump back to, are the potential
            // closure scope frames that might have been pushed if this subroutine was
            // called via a delegate reference.  Consume any of those that are in
            // the way, then expect the context record.  Any other pattern encountered
            // is proof the stack alignment got screwed up:
            bool okay;
            VariableScope peeked = cpu.PeekRaw(-1, out okay) as VariableScope;
            while (okay && peeked != null && peeked.IsClosure)
            {
                cpu.PopAboveStack(1);
                peeked = cpu.PeekRaw(-1, out okay) as VariableScope;
            }
            object shouldBeContextRecord = cpu.PopAboveStack(1);
            if ( !(shouldBeContextRecord is SubroutineContext) )
            {
                // This should never happen with any user code:
                throw new Exception( "kOS internal error: Stack misalignment detected when returning from routine.");
            }

            var contextRecord = shouldBeContextRecord as SubroutineContext;

            // Special case for when the subroutine was really being called as an interrupt
            // trigger from the kOS CPU itself.  In that case we don't want to leave the
            // return value atop the stack, and instead want to pop it and use it to decide
            // whether or not the re-insert the trigger for next time:
            if (contextRecord.IsTrigger)
            {
                cpu.PopStack(); // already got the return value up above, just ignore it.
                if (returnVal is bool || returnVal is BooleanValue )
                    if (Convert.ToBoolean(returnVal))
                        cpu.AddTrigger(contextRecord.TriggerPointer);
            }

            int destinationPointer = contextRecord.CameFromInstPtr;
            int currentPointer = cpu.InstructionPointer;
            DeltaInstructionPointer = destinationPointer - currentPointer;
        }
Пример #26
0
        public override void Execute(ICpu cpu)
        {
            object value = cpu.PopValue();
            string suffixName = cpu.PopStack().ToString().ToUpper();
            object popValue = cpu.PopValue();

            var specialValue = popValue as ISuffixed;
            if (specialValue == null)
            {
                // Box strings if necessary to allow suffixes
                var s = popValue as string;
                if (s != null)
                {
                    specialValue = new StringValue(s);
                }
                else
                {
                    throw new Exception(string.Format("Values of type {0} cannot have suffixes", popValue.GetType()));
                }
            }

            if (!specialValue.SetSuffix(suffixName, value))
            {
                throw new Exception(string.Format("Suffix {0} not found on object", suffixName));
            }
        }
Пример #27
0
 /// <summary>
 /// A utility function that will do a cpu.PopValue, but with an additional check to ensure
 /// the value atop the stack isn't the arg bottom marker.
 /// </summary>
 /// <returns>object popped if it all worked fine</returns>
 protected object PopValueAssert(ICpu cpu, bool barewordOkay = false)
 {
     object returnValue = cpu.PopValue(barewordOkay);
     if (returnValue != null && returnValue.GetType() == OpcodeCall.ArgMarkerType)
         throw new KOSArgumentMismatchException("Called with not enough arguments.");
     return returnValue;
 }
Пример #28
0
        public override void Execute(ICpu cpu)
        {
            object index = cpu.PopValue();
            object list = cpu.PopValue();

            object value;

            var indexable = list as IIndexable;
            if (indexable != null)
            {
                value = indexable.GetIndex(index);
            }
            // Box strings if necessary to allow them to be indexed
            else if (list is string)
            {
                value = new StringValue((string) list).GetIndex(index);
            }
            else
            {
                throw new Exception(string.Format("Can't iterate on an object of type {0}", list.GetType()));
            }

            if (!(list is IIndexable)) throw new Exception(string.Format("Can't iterate on an object of type {0}", list.GetType()));

            cpu.PushStack(value);
        }
Пример #29
0
 public override void Execute(ICpu cpu)
 {
     bool condition = Convert.ToBoolean(cpu.PopValue());
     DeltaInstructionPointer = condition ? Distance : 1;
 }
Пример #30
0
        public override void Execute(ICpu cpu)
        {
            object value = cpu.PopValue();
            object index = cpu.PopValue();
            object list = cpu.PopValue();

            if (index == null || value == null)
            {
                throw new KOSException("Neither the key nor the index of a collection may be null");
            }

            var indexable = list as IIndexable;
            if (indexable == null)
            {
                throw new KOSException(string.Format("Can't set indexed elements on an object of type {0}", list.GetType()));
            }
            indexable.SetIndex(index, value);
        }
        public void Invoke(ICpu cpu)
        {
            MethodInfo methInfo = del.Method;

            ParameterInfo[] paramArray     = methInfo.GetParameters();
            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 < paramArray.Length; ++i)
            {
                object        arg       = cpu.PopValue();
                Type          argType   = arg.GetType();
                ParameterInfo paramInfo = paramArray[i];

                // If this is the lastmost parameter then it might be a 'param' array which expects all the rest of
                // the arguments to be collected together into one single array parameter when invoking the method:
                isParamArrayArg = (i == paramArray.Length - 1 && Attribute.IsDefined(paramInfo, typeof(ParamArrayAttribute)));

                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(paramArray.Length, paramArray.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 = (isParamArrayArg ? 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}.", (paramArray.Length - i), arg, methInfo.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.GetStackSize() > 0 && !foundArgMarker)
                {
                    object marker = cpu.PopValue();
                    if (marker != null && marker.GetType() == CpuUtility.ArgMarkerType)
                    {
                        foundArgMarker = true;
                    }
                    else
                    {
                        ++numExtraArgs;
                    }
                }
                if (numExtraArgs > 0)
                {
                    throw new KOSArgumentMismatchException(paramArray.Length, paramArray.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;

            try
            {
                // I could find no documentation on what DynamicInvoke returns when the delegate
                // is a function returning void.  Does it return a null?  I don't know.  So to avoid the
                // problem, I split this into these two cases:
                if (methInfo.ReturnType == typeof(void))
                {
                    del.DynamicInvoke(argArray);
                    value = ScalarValue.Create(0);
                    // By adding this we can unconditionally assume all functionshave a return value
                    // to be used or popped away, even if "void".  In order to mainain consistency with
                    // the void return value of functions, and to ensure that we don't accidentally pass
                    // a value back to the user that they cannot interact with (null), we return zero.
                }
                else
                {
                    // Convert a primitive return type to a structure.  This is done in the opcode, since
                    // the opcode calls the deligate directly and cannot be (quickly) intercepted
                    value = Structure.FromPrimitiveWithAssert(del.DynamicInvoke(argArray));
                }
            }
            catch (TargetInvocationException e)
            {
                // Annoyingly, calling DynamicInvoke on a delegate wraps any exceptions the delegate throws inside
                // this TargetInvocationException, which hides them from the kOS user unless we do this:
                if (e.InnerException != null)
                {
                    throw e.InnerException;
                }
                throw;
            }
        }
Пример #32
0
        public override void Execute(ICpu cpu)
        {
            object value = cpu.PopValue();
            object index = cpu.PopValue();
            object list = cpu.PopValue();
            if (index is double || index is float)
            {
                index = Convert.ToInt32(index);  // allow expressions like (1.0) to be indexes
            }
            if (!(list is IIndexable)) throw new Exception(string.Format("Can't iterate on an object of type {0}", list.GetType()));
            if (!(index is int)) throw new Exception("The index must be an integer number");

            if (value != null)
            {
                ((IIndexable)list).SetIndex((int)index, value);
            }
        }