private static void GenerateFunction(FunctionNode node, DataContext data) { Append(data, "new ScriptFunction(new Func<ScriptExecutionContext, dynamic[]"); if (node.Args.Count > 0) { Append(data, ", "); Append(data, string.Join(", ", Enumerable.Repeat("dynamic", node.Args.Count))); } string functionName = "Function" + data.GetNextFunctionIndex(); Append(data, ", dynamic>({0})", functionName); data.VariableMap.PushFrame(); Append(data, ", "); if (node.Args.Count > 0) { Append(data, "new dynamic[] {{ "); for (int i = 0; i < node.Args.Count; i++) { data.VariableMap.DeclareVariable(node.Args[i].VariableName, node.Line, node.Column); if (i != 0) Append(data, ", "); if (node.Args[i].ValueExpression != null) { GenerateExpression(node.Args[i].ValueExpression, data); } else { Append(data, "null"); } } Append(data, " }}"); } else { Append(data, "null"); } var variableDeclarations = node.BodyStatementBlock.Find<VariableDeclarationNode>().Where(x => x.FindParent<FunctionNode>() == node).Select(x => x.VariableName); var capturedVariables = node.BodyStatementBlock.Find<VariableReferenceNode>() .Where(x => x.FindParent<FunctionNode>() == node && !variableDeclarations.Contains(x.VariableName) && !data.VariableMap.IsVariableDeclaredInCurrentFrame(x.VariableName) ) .ToArray(); Append(data, ", "); if (capturedVariables.Length > 0) { Append(data, "new dynamic[] {{ "); for (int i = 0; i < capturedVariables.Length; i++) { if (i != 0) Append(data, ", "); Append(data, "context[\"{0}\"]", capturedVariables[i].VariableName); } Append(data, " }}"); } else { Append(data, "null"); } Append(data, ")"); data.PushCodeBlock(functionName); data.CodeBlock.Padding = 2; string returnType = "dynamic"; if (node.IsGenerator) returnType = "IEnumerable<dynamic>"; Append(data, "private static {0} {1}(ScriptExecutionContext context, dynamic[] captures", returnType, functionName); if (node.Args.Count > 0) { Append(data, ", "); Append(data, string.Join(", ", node.Args.Select(x => "dynamic " + data.VariableMap.GetVariableName(x.VariableName)))); } EndLine(data, ")"); WriteLine(data, "{{"); data.CodeBlock.Padding++; if (!node.IsGenerator) WriteLine(data, "context.PushStackFrame(\"{0}\");", !node.IsAnonymous ? node.FunctionName : "<anonymous>"); foreach (var arg in node.Args) WriteLine(data, "context.DeclareVariable(\"{0}\", {1});", arg.VariableName, data.VariableMap.GetVariableName(arg.VariableName)); for (int i = 0; i < capturedVariables.Length; i++) WriteLine(data, "context.DeclareVariable(\"{0}\", captures[{1}]);", capturedVariables[i].VariableName, i); if (!node.IsGenerator) { WriteLine(data, "bool isError = false;"); WriteLine(data, "try {{"); data.CodeBlock.Padding++; } GenerateStatementBlock(node.BodyStatementBlock, data, pushStackFrame: false); if (!(node.BodyStatementBlock.Last() is ReturnNode) && !node.IsGenerator) { WriteLine(data, "return null;"); } if (!node.IsGenerator) { data.CodeBlock.Padding--; WriteLine(data, "}} catch(Exception) {{"); data.CodeBlock.Padding++; WriteLine(data, "isError = true;"); WriteLine(data, "throw;"); data.CodeBlock.Padding--; WriteLine(data, "}} finally {{"); data.CodeBlock.Padding++; WriteLine(data, "if (!isError) {{"); data.CodeBlock.Padding++; WriteLine(data, "context.PopStackFrame();"); data.CodeBlock.Padding--; WriteLine(data, "}}"); data.CodeBlock.Padding--; WriteLine(data, "}}"); } data.CodeBlock.Padding--; WriteLine(data, "}}"); data.PopCodeBlock(); data.VariableMap.PopFrame(); }