public override Runtime.Object GenerateRuntimeObject() { // End = end flow immediately // Done = return from thread or instruct the flow that it's safe to exit if (isEnd) { return(Runtime.ControlCommand.End()); } if (isDone) { return(Runtime.ControlCommand.Done()); } runtimeDivert = new Runtime.Divert(); // Normally we resolve the target content during the // Resolve phase, since we expect all runtime objects to // be available in order to find the final runtime path for // the destination. However, we need to resolve the target // (albeit without the runtime target) early so that // we can get information about the arguments - whether // they're by reference - since it affects the code we // generate here. ResolveTargetContent(); CheckArgumentValidity(); // Passing arguments to the knot bool requiresArgCodeGen = arguments != null && arguments.Count > 0; if (requiresArgCodeGen || isFunctionCall || isTunnel || isThread) { var container = new Runtime.Container(); // Generate code for argument evaluation // This argument generation is coded defensively - it should // attempt to generate the code for all the parameters, even if // they don't match the expected arguments. This is so that the // parameter objects themselves are generated correctly and don't // get into a state of attempting to resolve references etc // without being generated. if (requiresArgCodeGen) { // Function calls already in an evaluation context if (!isFunctionCall) { container.AddContent(Runtime.ControlCommand.EvalStart()); } List <FlowBase.Argument> targetArguments = null; if (targetContent) { targetArguments = (targetContent as FlowBase).arguments; } for (var i = 0; i < arguments.Count; ++i) { Expression argToPass = arguments [i]; FlowBase.Argument argExpected = null; if (targetArguments != null && i < targetArguments.Count) { argExpected = targetArguments [i]; } // Pass by reference: argument needs to be a variable reference if (argExpected != null && argExpected.isByReference) { var varRef = argToPass as VariableReference; if (varRef == null) { Error("Expected variable name to pass by reference to 'ref " + argExpected.name + "' but saw " + argToPass.ToString()); break; } var varPointer = new Runtime.VariablePointerValue(varRef.name); container.AddContent(varPointer); } // Normal value being passed: evaluate it as normal else { argToPass.GenerateIntoContainer(container); } } // Function calls were already in an evaluation context if (!isFunctionCall) { container.AddContent(Runtime.ControlCommand.EvalEnd()); } } // Starting a thread? A bit like a push to the call stack below... but not. // It sort of puts the call stack on a thread stack (argh!) - forks the full flow. if (isThread) { container.AddContent(Runtime.ControlCommand.StartThread()); } // If this divert is a function call, tunnel, we push to the call stack // so we can return again else if (isFunctionCall || isTunnel) { runtimeDivert.pushesToStack = true; runtimeDivert.stackPushType = isFunctionCall ? Runtime.PushPopType.Function : Runtime.PushPopType.Tunnel; } // Jump into the "function" (knot/stitch) container.AddContent(runtimeDivert); return(container); } // Simple divert else { return(runtimeDivert); } }
public override void GenerateIntoContainer(Runtime.Container container) { innerExpression.GenerateIntoContainer(container); container.AddContent(Runtime.NativeFunctionCall.CallWithName(nativeNameForOp)); }