public static object Call(TemplateContext context, ScriptNode callerContext, object functionObject, bool processPipeArguments, List <ScriptExpression> arguments = null) { if (callerContext == null) { throw new ArgumentNullException(nameof(callerContext)); } if (functionObject == null) { throw new ScriptRuntimeException(callerContext.Span, $"The target function `{callerContext}` is null"); } var function = functionObject as ScriptFunction; var externFunction = functionObject as IScriptCustomFunction; if (function == null && externFunction == null) { throw new ScriptRuntimeException(callerContext.Span, $"Invalid target function `{callerContext}`( as `{functionObject?.GetType()}`)"); } ScriptBlockStatement blockDelegate = null; if (context.BlockDelegates.Count > 0) { blockDelegate = context.BlockDelegates.Pop(); } // We can't cache this array because it might be collect by the function // So we absolutely need to generate a new array everytime we call a function var argumentValues = new ScriptArray(); // Handle pipe arguments here if (processPipeArguments && context.PipeArguments.Count > 0) { argumentValues.AddRange(context.PipeArguments); context.PipeArguments.Clear(); } // Process direct arguments if (arguments != null) { foreach (var argument in arguments) { object value; // Handle named arguments var namedArg = argument as ScriptNamedArgument; if (namedArg != null) { // In case of a ScriptFunction, we write the named argument into the ScriptArray directly if (externFunction == null) { // We can't add an argument that is "size" for array if (argumentValues.CanWrite(namedArg.Name)) { argumentValues.SetValue(context, callerContext.Span, namedArg.Name, context.Evaluate(namedArg), false); continue; } // Otherwise pass as a regular argument value = context.Evaluate(namedArg); } else { // Named argument are passed as is to the IScriptCustomFunction value = argument; } } else { value = context.Evaluate(argument); } // Handle parameters expansion for a function call when the operator ^ is used var unaryExpression = argument as ScriptUnaryExpression; if (unaryExpression != null && unaryExpression.Operator == ScriptUnaryOperator.FunctionParametersExpand) { var valueEnumerator = value as IEnumerable; if (valueEnumerator != null) { foreach (var subValue in valueEnumerator) { argumentValues.Add(subValue); } continue; } } argumentValues.Add(value); } } object result = null; context.EnterFunction(callerContext); try { if (externFunction != null) { result = externFunction.Invoke(context, callerContext, argumentValues, blockDelegate); } else { context.SetValue(ScriptVariable.Arguments, argumentValues, true); // Set the block delegate if (blockDelegate != null) { context.SetValue(ScriptVariable.BlockDelegate, blockDelegate, true); } result = context.Evaluate(function.Body); } } finally { context.ExitFunction(); } // Restore the flow state to none context.FlowState = ScriptFlowState.None; return(result); }