protected override TemplateInfo GetTemplateInfo(string templateBody, Dictionary<string, Type> globalsTypes, Dictionary<string, string> inlineTemplates, List<Assembly> referencedAssemblies) { lock (m_CacheLock) { // Create template info TemplateInfo ti = new TemplateInfo() { TemplateBody = templateBody, TemplateHash = null, GlobalsTypes = globalsTypes, ReferencedAssemblies = referencedAssemblies, InlineTemplates = inlineTemplates, TemplateRenderMethod = null }; // Compile Template to TALPrograms and generate the TemplateHash TALCompiler.CompileTemplate(ti); // Generated template found in cache if (m_Cache.ContainsKey(ti.TemplateHash)) { return m_Cache[ti.TemplateHash]; } // Generate source SourceGenerator sourceGenerator = new SourceGenerator(); ti.GeneratedSourceCode = sourceGenerator.GenerateSource(ti); // Generate assembly AssemblyGenerator assemblyCompiler = new AssemblyGenerator(); Assembly assembly = assemblyCompiler.GenerateAssembly(ti, true, null, null); // Try to load the Render() method from assembly ti.TemplateRenderMethod = GetTemplateRenderMethod(assembly, ti); m_Cache.Add(ti.TemplateHash, ti); return ti; } }
private Dictionary<string, TemplateInfo> LoadTemplatesInfo(string cacheFolder) { Dictionary<string, TemplateInfo> templateCache = new Dictionary<string, TemplateInfo>(); // Load assemblies containing type with full name [<TEMPLATE_NAMESPACE>.<TEMPLATE_TYPENAME>], // with one public method [public static string Render(Dictionary<string, object> globals)] DirectoryInfo di = new DirectoryInfo(cacheFolder); foreach (FileInfo fi in di.GetFiles()) { Match fileNameMatch = m_PatternRex.Match(fi.Name); if (fileNameMatch.Success) { // Try to load file as assembly try { AssemblyName.GetAssemblyName(fi.FullName); } catch (System.BadImageFormatException) { // The file is not an Assembly continue; } // Read assembly Assembly assembly = Utils.ReadAssembly(fi.FullName); // Get template hash from file name string templateHash = fileNameMatch.Groups["key"].Value; // Create template info TemplateInfo ti = new TemplateInfo() { TemplateHash = templateHash }; // Try to load the Render() method from assembly ti.TemplateRenderMethod = GetTemplateRenderMethod(assembly, ti); templateCache.Add(templateHash, ti); } } return templateCache; }
public RenderTemplateException(TemplateInfo templateInfo, string message, Exception innerException) : base(message, innerException) { _templateInfo = templateInfo; }
protected override TemplateInfo GetTemplateInfo(string templateBody, Dictionary<string, Type> globalsTypes, Dictionary<string, string> inlineTemplates, List<Assembly> referencedAssemblies) { lock (m_CacheLock) { // Cache is empty, load templates from cache folder if (m_Cache == null) { m_Cache = LoadTemplatesInfo(m_CacheFolder); } // Create template info TemplateInfo ti = new TemplateInfo() { TemplateBody = templateBody, TemplateHash = null, GlobalsTypes = globalsTypes, ReferencedAssemblies = referencedAssemblies, InlineTemplates = inlineTemplates, TemplateRenderMethod = null }; // Compile Template to TALPrograms and generate the TemplateHash TALCompiler.CompileTemplate(ti); // Generated template found in cache if (m_Cache.ContainsKey(ti.TemplateHash)) { return m_Cache[ti.TemplateHash]; } // Path to output assembly string assemblyFileName = m_Pattern.Replace("{key}", ti.TemplateHash); string assemblyPath = Path.Combine(m_CacheFolder, assemblyFileName); // Generate source SourceGenerator sourceGenerator = new SourceGenerator(); ti.GeneratedSourceCode = sourceGenerator.GenerateSource(ti); // Generate assembly AssemblyGenerator assemblyCompiler = new AssemblyGenerator(); Assembly assembly = assemblyCompiler.GenerateAssembly(ti, false, assemblyPath, null); // Try to load the Render() method from assembly ti.TemplateRenderMethod = GetTemplateRenderMethod(assembly, ti); m_Cache.Add(ti.TemplateHash, ti); return ti; } }
static string ComputeTemplateHash(TemplateInfo ti) { // Template Hash is computed from following parts (and is recalculated if any of the parts is changed): // "Template Body" // "Full Names of Global Types" // "Inline Templates Bodies" // "Imported Templates Bodies" // "Full Names of Referenced Assemblies" // Global types string globalTypes = ""; if (ti.GlobalsTypes != null && ti.GlobalsTypes.Count > 0) { List<string> keys = new List<string>(ti.GlobalsTypes.Keys); keys.Sort(); foreach (string varName in keys) { Type type = ti.GlobalsTypes[varName]; globalTypes += varName + type.FullName; } } // Inline templates hashes string inlineTemplatesHashes = ""; if (ti.InlineTemplates != null && ti.InlineTemplates.Count > 0) { List<string> keys = new List<string>(ti.InlineTemplates.Keys); keys.Sort(); foreach (string templateName in keys) { inlineTemplatesHashes += Utils.ComputeHash(ti.InlineTemplates[templateName]); } } // Imported templates hashes string importedTemplatesHashes = ""; if (ti.ImportedPrograms != null && ti.ImportedPrograms.Count > 0) { List<string> keys = new List<string>(ti.ImportedPrograms.Keys); keys.Sort(); foreach (string path in keys) { importedTemplatesHashes += Utils.ComputeHash(ti.ImportedPrograms[path].Source); } } // Referenced Assemblies string referencedAssemblies = ""; if (ti.ReferencedAssemblies != null && ti.ReferencedAssemblies.Count > 0) { foreach (Assembly asm in ti.ReferencedAssemblies) { referencedAssemblies += asm.FullName; } } string templateHash = Utils.ComputeHash(Utils.ComputeHash(ti.TemplateBody) + globalTypes + inlineTemplatesHashes + importedTemplatesHashes + referencedAssemblies); return templateHash; }
public Assembly GenerateAssembly(TemplateInfo ti, bool generateInMemory, string assemblyPath, string keyFileName) { //--------------------------- // Create compiler parameters //--------------------------- var compilerParameters = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = generateInMemory, IncludeDebugInformation = false, WarningLevel = 4, TreatWarningsAsErrors = false, TempFiles = new TempFileCollection(Path.GetTempPath(), false), OutputAssembly = assemblyPath }; if (!string.IsNullOrEmpty(keyFileName)) { compilerParameters.CompilerOptions = string.Format("/keyfile:{0}", keyFileName); } //--------------------------- // Setup referenced assemblies for code compiler //--------------------------- // Add core assemblies var assemblies = new List <string> { "System.dll", "System.Core.dll", "System.Security.dll", typeof(Template).Assembly.Location }; foreach (string asmName in assemblies) { compilerParameters.ReferencedAssemblies.Add(asmName); } // Add assemblies where global types are declared if (ti.GlobalsTypes != null) { foreach (string varName in ti.GlobalsTypes.Keys) { Type type = ti.GlobalsTypes[varName]; if (type != null) { var genericTypeArguments = new List <Type>(Utils.GetGenericTypeArguments(type)); List <Assembly> asmList = genericTypeArguments.Select(t => t.Assembly).ToList(); foreach (var assembly in asmList) { if (!assemblies.Contains(assembly.Location) && !assemblies.Contains(Path.GetFileName(assembly.Location))) { compilerParameters.ReferencedAssemblies.Add(assembly.Location); assemblies.Add(assembly.Location); // Referenced assemblies foreach (AssemblyName refAssemblyName in assembly.GetReferencedAssemblies()) { Assembly refAssembly = AppDomain.CurrentDomain.Load(refAssemblyName); if (!assemblies.Contains(refAssembly.Location) && !assemblies.Contains(Path.GetFileName(refAssembly.Location))) { compilerParameters.ReferencedAssemblies.Add(refAssembly.Location); assemblies.Add(refAssembly.Location); } } } } } } } // Add assemblies from referencedAssemblies list if (ti.ReferencedAssemblies != null) { foreach (Assembly refAsm in ti.ReferencedAssemblies) { if (!assemblies.Contains(refAsm.Location) && !assemblies.Contains(Path.GetFileName(refAsm.Location))) { compilerParameters.ReferencedAssemblies.Add(refAsm.Location); } } } //--------------------------- // Compile //--------------------------- string compilerVersion = "v3.5"; if (Environment.Version.Major > 3) { compilerVersion = "v4.0"; } var providerOptions = new Dictionary <string, string> { { "CompilerVersion", compilerVersion } }; using (CodeDomProvider provider = new CSharpCodeProvider(providerOptions)) { CompilerResults compilerResults = provider.CompileAssemblyFromSource(compilerParameters, ti.GeneratedSourceCode); if (compilerResults.Errors.HasErrors) { var exceptionBuilder = new StringBuilder("Compilation has failed with following errors:\n============\n"); string[] sourceLines = ti.GeneratedSourceCode.Split('\n'); foreach (CompilerError error in compilerResults.Errors) { exceptionBuilder.AppendLine(error.ErrorNumber + ": " + error.ErrorText + " (line " + error.Line + ")"); // Add source lines for context int firstLine = Math.Max(0, error.Line - 2); int secondLine = Math.Min(sourceLines.Length, error.Line + 2); for (int i = firstLine; i <= secondLine; i++) { exceptionBuilder.AppendLine(sourceLines[i].Trim()); } exceptionBuilder.AppendLine("------------------"); } throw new CompileSourceException(ti, compilerResults.Errors, exceptionBuilder.ToString()); } return(compilerResults.CompiledAssembly); } }
/// <summary> /// Render the template /// </summary> /// <param name="templateBody">The template body</param> /// <param name="globals">Dictionary of global variables</param> /// <param name="inlineTemplates">Dictionary of inline templates</param> /// <param name="referencedAssemblies">List of referenced assemblies</param> /// <param name="sourceCode">Template source code</param> /// <param name="culture">Culture to use for string conversions. Default is invariant culture.</param> /// <returns>Rendered template</returns> public string RenderTemplate(string templateBody, Dictionary<string, object> globals, Dictionary<string, string> inlineTemplates, List<Assembly> referencedAssemblies, out TemplateInfo templateInfo) { return RenderTemplate(templateBody, globals, inlineTemplates, referencedAssemblies, out templateInfo, CultureInfo.InvariantCulture); }
public Assembly GenerateAssembly(TemplateInfo ti, bool generateInMemory, string assemblyPath, string keyFileName) { //--------------------------- // Create compiler parameters //--------------------------- var compilerParameters = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = generateInMemory, IncludeDebugInformation = false, WarningLevel = 4, TreatWarningsAsErrors = false, TempFiles = new TempFileCollection(Path.GetTempPath(), false), OutputAssembly = assemblyPath }; if (!string.IsNullOrEmpty(keyFileName)) { compilerParameters.CompilerOptions = string.Format("/keyfile:{0}", keyFileName); } //--------------------------- // Setup referenced assemblies for code compiler //--------------------------- // Add core assemblies var assemblies = new List<string> { "System.dll", "System.Core.dll", "System.Security.dll", typeof(Template).Assembly.Location }; foreach (string asmName in assemblies) compilerParameters.ReferencedAssemblies.Add(asmName); // Add assemblies where global types are declared if (ti.GlobalsTypes != null) { foreach (string varName in ti.GlobalsTypes.Keys) { Type type = ti.GlobalsTypes[varName]; if (type != null) { var genericTypeArguments = new List<Type>(Utils.GetGenericTypeArguments(type)); List<Assembly> asmList = genericTypeArguments.Select(t => t.Assembly).ToList(); foreach (var assembly in asmList) { if (!assemblies.Contains(assembly.Location) && !assemblies.Contains(Path.GetFileName(assembly.Location))) { compilerParameters.ReferencedAssemblies.Add(assembly.Location); assemblies.Add(assembly.Location); // Referenced assemblies foreach (AssemblyName refAssemblyName in assembly.GetReferencedAssemblies()) { Assembly refAssembly = AppDomain.CurrentDomain.Load(refAssemblyName); if (!assemblies.Contains(refAssembly.Location) && !assemblies.Contains(Path.GetFileName(refAssembly.Location))) { compilerParameters.ReferencedAssemblies.Add(refAssembly.Location); assemblies.Add(refAssembly.Location); } } } } } } } // Add assemblies from referencedAssemblies list if (ti.ReferencedAssemblies != null) { foreach (Assembly refAsm in ti.ReferencedAssemblies) { if (!assemblies.Contains(refAsm.Location) && !assemblies.Contains(Path.GetFileName(refAsm.Location))) { compilerParameters.ReferencedAssemblies.Add(refAsm.Location); } } } //--------------------------- // Compile //--------------------------- string compilerVersion = "v3.5"; if (Environment.Version.Major > 3) { compilerVersion = "v4.0"; } var providerOptions = new Dictionary<string, string> { { "CompilerVersion", compilerVersion } }; using (CodeDomProvider provider = new CSharpCodeProvider(providerOptions)) { CompilerResults compilerResults = provider.CompileAssemblyFromSource(compilerParameters, ti.GeneratedSourceCode); if (compilerResults.Errors.HasErrors) { var exceptionBuilder = new StringBuilder("Compilation has failed with following errors:\n============\n"); string[] sourceLines = ti.GeneratedSourceCode.Split('\n'); foreach (CompilerError error in compilerResults.Errors) { exceptionBuilder.AppendLine(error.ErrorNumber + ": " + error.ErrorText + " (line " + error.Line + ")"); // Add source lines for context int firstLine = Math.Max(0, error.Line - 2); int secondLine = Math.Min(sourceLines.Length, error.Line + 2); for (int i = firstLine; i <= secondLine; i++) { exceptionBuilder.AppendLine(sourceLines[i].Trim()); } exceptionBuilder.AppendLine("------------------"); } throw new CompileSourceException(ti, compilerResults.Errors, exceptionBuilder.ToString()); } return compilerResults.CompiledAssembly; } }
/// <summary> /// Render the template /// </summary> /// <param name="output">The output stream</param> /// <param name="templateBody">The template body</param> /// <param name="globals">Dictionary of global variables</param> /// <param name="inlineTemplates">Dictionary of inline templates</param> /// <param name="referencedAssemblies">List of referenced assemblies</param> /// <param name="sourceCode">Template source code</param> public void RenderTemplate(StreamWriter output, string templateBody, Dictionary<string, object> globals, Dictionary<string, string> inlineTemplates, List<Assembly> referencedAssemblies, out TemplateInfo templateInfo) { RenderTemplate(output, templateBody, globals, inlineTemplates, referencedAssemblies, out templateInfo, CultureInfo.InvariantCulture); }
/// <summary> /// Render the template /// </summary> /// <param name="output">The output stream</param> /// <param name="templateBody">The template body</param> /// <param name="globals">Dictionary of global variables</param> /// <param name="inlineTemplates">Dictionary of inline templates</param> /// <param name="referencedAssemblies">List of referenced assemblies</param> /// <param name="sourceCode">Template source code</param> /// <param name="culture">Culture to use for string conversions. Default is invariant culture.</param> public void RenderTemplate(StreamWriter output, string templateBody, Dictionary<string, object> globals, Dictionary<string, string> inlineTemplates, List<Assembly> referencedAssemblies, out TemplateInfo templateInfo, CultureInfo culture) { templateInfo = null; if (string.IsNullOrEmpty(templateBody)) { return; } Dictionary<string, Type> globalsTypes = new Dictionary<string, Type>(); if (globals != null) { foreach (string objName in globals.Keys) { object obj = globals[objName]; globalsTypes.Add(objName, obj != null ? obj.GetType() : null); } } // Get template info from cache templateInfo = GetTemplateInfo(templateBody, globalsTypes, inlineTemplates, referencedAssemblies); // Call the Render() method try { templateInfo.TemplateRenderMethod.Invoke(null, new object[] { output, globals, culture }); } catch (TargetInvocationException ex) { throw new RenderTemplateException(templateInfo, ex.InnerException.Message, ex.InnerException); } catch (Exception ex) { throw new RenderTemplateException(templateInfo, ex.Message, ex); } }
public string GenerateCode(TemplateInfo ti) { _scopeStack = new Stack<Scope>(); //---------------- // Process globals //---------------- if (ti.GlobalsTypes != null) { foreach (string varName in ti.GlobalsTypes.Keys) { Type type = ti.GlobalsTypes[varName]; if (type != null) { string typeName = ""; if (Utils.IsDynamicType(type)) { typeName = "dynamic"; } else { typeName = Utils.GetFullTypeName(type, _typeNamesCache); } _globalNames.Add(varName); WriteToGlobals(@"var {1} = ({0})context[""{1}""];", typeName, varName); } } } //----------------------- // Process macro commands //----------------------- var programNamespaces = new List<string>(); // Process main program macro commands ProcessProgramMacros(ti.MainProgram, MainProgramNamespace, programNamespaces); // Process imported programs macro commands foreach (string destNs in ti.ImportedNamespaces.Keys) { foreach (string templatePath in ti.ImportedNamespaces[destNs]) { Program importedProgram = ti.ImportedPrograms[templatePath]; ProcessProgramMacros(importedProgram, destNs, programNamespaces); } } // Main template macros are also in the global namespace WriteToGlobals(@"macros = {0}.macros;", MainProgramNamespace); //-------------------------------------------------------- // Process main program commands (ignoring macro commands) //-------------------------------------------------------- WriteToBody(@"//======================="); WriteToBody(@"// Main template program:"); WriteToBody(@"//======================="); WriteToBody(@""); WriteToBody(@"__CleanProgram();"); WriteToBody(@""); HandleCommands(ti.MainProgram.ProgramCommands); //---------------------------- // Resolve required namespaces //---------------------------- // Default namespaces var namespacesList = new List<string> { "System", "System.IO", "System.Linq", "System.Text", "System.Text.RegularExpressions", "System.Collections", "System.Collections.Generic", "System.Security.Permissions", "System.Security", "System.Globalization", "SharpTAL", }; // Find all namespaces with extension methods in assemblies where global types are defined var assemblies = new List<string>(); if (ti.GlobalsTypes != null) { foreach (string varName in ti.GlobalsTypes.Keys) { Type type = ti.GlobalsTypes[varName]; if (type != null) { if (!assemblies.Contains(type.Assembly.Location) && !assemblies.Contains(Path.GetFileName(type.Assembly.Location))) { // Check if assembly has defined "ExtensionAttribute" Utils.GetExtensionMethodNamespaces(type.Assembly, namespacesList); assemblies.Add(type.Assembly.Location); // Referenced assemblies foreach (AssemblyName assemblyName in type.Assembly.GetReferencedAssemblies()) { Assembly assembly = AppDomain.CurrentDomain.Load(assemblyName); if (!assemblies.Contains(assembly.Location) && !assemblies.Contains(Path.GetFileName(assembly.Location))) { Utils.GetExtensionMethodNamespaces(assembly, namespacesList); assemblies.Add(assembly.Location); } } } } } } // Find all namespaces with extension methods in referenced assemblies if (ti.ReferencedAssemblies != null) { foreach (Assembly refAsm in ti.ReferencedAssemblies) { if (!assemblies.Contains(refAsm.Location) && !assemblies.Contains(Path.GetFileName(refAsm.Location))) { Utils.GetExtensionMethodNamespaces(refAsm, namespacesList); assemblies.Add(refAsm.Location); } } } // Create list of "usings" string usings = ""; foreach (string ns in namespacesList) { usings = string.Format("{0}using {1};{2}", usings, ns, Environment.NewLine); } //------------------------- // Generate template source //------------------------- string templateSource = FileBodyTemplate. Replace("${generator_version}", GetType().Assembly.GetName().Version.ToString()). Replace("${defaultvalue}", Constants.DefaultValue). Replace("${usings}", usings). Replace("${template_hash}", ti.TemplateKey). Replace("${globals}", _globalsBody.ToString()). Replace("${body}", _rendererBody.ToString()); return templateSource; }
public CompileSourceException(TemplateInfo templateInfo, CompilerErrorCollection errors, string message) : base(message) { _templateInfo = templateInfo; _errors = errors; }
public static void CompileTemplate(TemplateInfo ti) { ti.Programs = new Dictionary<string, TALProgram>(); ti.ImportedPrograms = new Dictionary<string, TALProgram>(); ti.ImportedNamespaces = new Dictionary<string, HashSet<string>>(); TALCompiler compiler = new TALCompiler(); // Compile main template TALProgram mainProg = compiler.Compile(ti.TemplateBody, "<main template>"); ti.Programs.Add("template", mainProg); // Compile imports of main template CompileImports(compiler, mainProg, ti); // Compile inline templates if (ti.InlineTemplates != null) { foreach (string key in ti.InlineTemplates.Keys) { TALProgram inlineProg = compiler.Compile(ti.InlineTemplates[key], string.Format("<inline template: {0}>", key)); ti.Programs.Add(key, inlineProg); // Compile Imports of inline template CompileImports(compiler, inlineProg, ti); } } // Compute template hash ti.TemplateHash = ComputeTemplateHash(ti); }
void Recompile(Dictionary<string, object> globals) { if (globals != null && globals.Count > 0) { _globalsTypes = new Dictionary<string, Type>(); foreach (string name in globals.Keys) { object obj = globals[name]; _globalsTypes.Add(name, obj != null ? obj.GetType() : null); } } _templateInfo = _templateCache.CompileTemplate(_body, _globalsTypes, _referencedAssemblies); }
/// <summary> /// Render the template /// </summary> /// <param name="templateBody">The template body</param> /// <param name="globals">Dictionary of global variables</param> /// <param name="inlineTemplates">Dictionary of inline templates</param> /// <param name="referencedAssemblies">List of referenced assemblies</param> /// <param name="sourceCode">Template source code</param> /// /// <returns>Rendered template</returns> public string RenderTemplate(string templateBody, Dictionary<string, object> globals, Dictionary<string, string> inlineTemplates, List<Assembly> referencedAssemblies, out TemplateInfo templateInfo, CultureInfo culture) { // Expand template MemoryStream stream = new MemoryStream(); StreamWriter writer = new StreamWriter(stream); RenderTemplate(writer, templateBody, globals, inlineTemplates, referencedAssemblies, out templateInfo, culture); writer.Flush(); // Reset stream position stream.Position = 0; // Read stream to string StreamReader reader = new StreamReader(stream); string result = reader.ReadToEnd(); writer.Close(); return result; }
protected MethodInfo GetTemplateRenderMethod(Assembly assembly, TemplateInfo ti) { string templateTypeFullName = string.Format("Templates.Template_{0}", ti.TemplateHash); // Check if assembly contains the template type Type templateType = assembly.GetType(templateTypeFullName); if (templateType == null) { throw new Exception(string.Format("Failed to find type [{0}] in assembly [{1}].", templateTypeFullName, assembly.FullName)); } // Check if the template type has public method [static void Render(StreamWriter output, Dictionary<string, object>)] MethodInfo renderMethod = templateType.GetMethod("Render", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(StreamWriter), typeof(Dictionary<string, object>), typeof(CultureInfo) }, null); if (renderMethod == null || renderMethod.ReturnType.FullName != "System.Void") { throw new Exception(string.Format(@"Failed to find Render method in type [{0}] in assembly [{1}]. The signature of method must be [static void Render(StreamWriter output, Dictionary<string, object>, CultureInfo culture)]", templateTypeFullName, assembly.FullName)); } return renderMethod; }
static void CompileImports(TALCompiler compiler, TALProgram program, TemplateInfo ti) { if (program.Imports != null && program.Imports.Count > 0) { foreach (string key in program.Imports) { // Split the Import key string destNs = key.Split(new char[] { ':' }, 2)[0]; string sourcePath = key.Split(new char[] { ':' }, 2)[1]; // Imported macros without namespace go into main template namespace. if (string.IsNullOrEmpty(destNs)) { destNs = "template"; } // Check if the template on path was not compiled TALProgram importedProg = null; if (!ti.ImportedPrograms.ContainsKey(sourcePath)) { // Compile Imported template string source = File.ReadAllText(sourcePath); importedProg = compiler.Compile(source, sourcePath); ti.ImportedPrograms.Add(sourcePath, importedProg); // Compile Imports of Imported template CompileImports(compiler, importedProg, ti); } else { importedProg = ti.ImportedPrograms[sourcePath]; } // Save info about Imported program by namespace and path if (!ti.ImportedNamespaces.ContainsKey(destNs)) { ti.ImportedNamespaces.Add(destNs, new HashSet<string>() { sourcePath }); } else { if (!ti.ImportedNamespaces[destNs].Contains(sourcePath)) { ti.ImportedNamespaces[destNs].Add(sourcePath); } } } } }