/// <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); } }
/// <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); }
public void CanGetListIndex() { var list = new ListValue(); list.Add(new StringValue("bar")); cpu.PushStack(list); const int INDEX = 0; cpu.PushStack(INDEX); var opcode = new OpcodeGetIndex(); opcode.Execute(cpu); Assert.AreEqual(1, list.Count()); Assert.AreEqual(new StringValue("bar"), cpu.PopStack()); }
/// <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; }
public override void Execute(ICpu cpu) { object identifier = cpu.PopStack(); if (identifier != null) { cpu.RemoveVariable(identifier.ToString()); } else { throw new KOSDeprecationException("0.17","UNSET ALL", "<not supported anymore now that we have nested scoping>", ""); } }
public override void Execute(ICpu cpu) { object value1 = cpu.PopStack(); object value2 = cpu.PopStack(); cpu.PushStack(value1); cpu.PushStack(value2); }
public override void Execute(ICpu cpu) { Structure value = PopStructureAssertEncapsulated(cpu); // Convert to string instead of cast in case the identifier is stored // as an encapsulated StringValue, preventing an unboxing collision. var identifier = Convert.ToString(cpu.PopStack()); cpu.SetNewLocal(identifier, value); }
public override void Execute(ICpu cpu) { Structure value = cpu.PopStructureEncapsulated(); // new value to set it to string suffixName = cpu.PopStack().ToString().ToUpper(); // name of suffix being set Structure popValue = cpu.PopStructureEncapsulated(); // object to which the suffix is attached. // We aren't converting the popValue to a Scalar, Boolean, or String structure here because // the referenced variable wouldn't be updated. The primitives themselves are treated as value // types instead of reference types. This is also why I removed the string unboxing // from the ISuffixed check below. var specialValue = popValue as ISuffixed; if (specialValue == null) { throw new Exception(string.Format("Values of type {0} cannot have suffixes", popValue.GetType())); } // TODO: When we refactor to make every structure use the new suffix style, this conversion // to primative can be removed. Right now there are too many structures that override the // SetSuffix method while relying on unboxing the object rahter than using Convert if (!specialValue.SetSuffix(suffixName, Structure.ToPrimitive(value))) { throw new Exception(string.Format("Suffix {0} not found on object", suffixName)); } }
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; }
public override void Execute(ICpu cpu) { object value = cpu.PopValue(); var identifier = (string)cpu.PopStack(); cpu.SetNewLocal(identifier, value); }
public override void Execute(ICpu cpu) { bool result = false; //pessimistic default // Convert to string instead of cast in case the identifier is stored // as an encapsulated StringValue, preventing an unboxing collision. string ident = Convert.ToString(cpu.PopStack()); if (ident != null && cpu.IdentifierExistsInScope(ident)) { result = true; } cpu.PushStack(result); }
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)); } }
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); }
public override void Execute(ICpu cpu) { object value = PopValueAssert(cpu); var identifier = (string)cpu.PopStack(); cpu.SetGlobal(identifier, value); }
public override void Execute(ICpu cpu) { bool result = false; //pessimistic default string ident = cpu.PopStack() as string; if (ident != null && cpu.IdentifierExistsInScope(ident)) { result = true; } cpu.PushStack(result); }
public override void Execute(ICpu cpu) { string suffixName = cpu.PopStack().ToString().ToUpper(); object popValue = cpu.PopValueEncapsulated(); var specialValue = popValue as ISuffixed; if (specialValue == null) { throw new Exception(string.Format("Values of type {0} cannot have suffixes", popValue.GetType())); } ISuffixResult result = specialValue.GetSuffix(suffixName); // If the result is a suffix that is still in need of being invoked and hasn't resolved to a value yet: if (result != null && !IsMethodCallAttempt && !result.HasValue) { // 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(result); cpu.PushStack(new KOSArgMarkerType()); OpcodeCall.StaticExecute(cpu, false, "", false); // this will push the return value on the stack for us. } else { if (result.HasValue) { // Push the already calculated value. cpu.PushStack(result.Value); } else { // Push the indirect suffix delegate, but don't execute it yet // because we need to put the upcoming arg list above it on the stack. // Eventually an <indirect> OpcodeCall will occur further down the program which // will actually execute this. cpu.PushStack(result); } } }
public override void Execute(ICpu cpu) { // Return value should be atop the stack - we have to pop it so that // we can reach the arg start marker under it: object returnVal = cpu.PopValue(); // The next thing on the stack under the return value should be the marker that indicated where // the parameters started. It should be thrown away now. If the next thing is NOT the marker // of where the parameters started, that is proof the stack is misaligned, probably because the // number of args passed didn't match the number of DECLARE PARAMETER statements in the function: string shouldBeArgMarker = cpu.PopStack() as string; if ( (shouldBeArgMarker == null) || (!(shouldBeArgMarker.Equals(OpcodeCall.ARG_MARKER_STRING))) ) { throw new KOSArgumentMismatchException( string.Format("(detected when returning from function and the stack still had {0} on it)", (shouldBeArgMarker ?? "a non-string value")) ); } // If the proper argument marker was found, then it's all okay, so put the return value // back, where it belongs, now that the arg start marker was popped off: cpu.PushStack(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; int destinationPointer = contextRecord.CameFromInstPtr; int currentPointer = cpu.InstructionPointer; DeltaInstructionPointer = destinationPointer - currentPointer; }