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);
}
}