From 4dca6c80ec4808660b846362e9fe7f16dbffb1bf Mon Sep 17 00:00:00 2001 From: Mike Corsaro Date: Thu, 29 Nov 2018 10:56:44 -0800 Subject: [PATCH 1/2] Added a default 1 second timeout to regex parsing for problematic CSS parsing --- ColorCode.Core/Compilation/LanguageCompiler.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ColorCode.Core/Compilation/LanguageCompiler.cs b/ColorCode.Core/Compilation/LanguageCompiler.cs index 53fde61..92266df 100644 --- a/ColorCode.Core/Compilation/LanguageCompiler.cs +++ b/ColorCode.Core/Compilation/LanguageCompiler.cs @@ -12,6 +12,7 @@ namespace ColorCode.Compilation public class LanguageCompiler : ILanguageCompiler { private static readonly Regex numberOfCapturesRegex = new Regex(@"(?x)(? compiledLanguages; private readonly ReaderWriterLockSlim compileLock; @@ -103,7 +104,7 @@ private static void CompileRules(IList rules, out Regex regex, out for (int i = 1; i < rules.Count; i++) CompileRule(rules[i], regexBuilder, captures, false); - regex = new Regex(regexBuilder.ToString()); + regex = new Regex(regexBuilder.ToString(), RegexOptions.None, defaultRegexParseTimeout); } private static void CompileRule(LanguageRule languageRule, StringBuilder regex, ICollection captures, bool isFirstRule) From 76b00db27f4da11770c98f49ef81b593d2dda664 Mon Sep 17 00:00:00 2001 From: Mike Corsaro Date: Thu, 29 Nov 2018 11:28:49 -0800 Subject: [PATCH 2/2] Refactored default parse timeout to constructor in `CodeColorizerBase` --- ColorCode.Core/CodeColorizerBase.cs | 6 ++++-- .../Compilation/ILanguageCompiler.cs | 2 +- ColorCode.Core/Compilation/LanguageCompiler.cs | 13 ++++++------- ColorCode.Core/Parsing/LanguageParser.cs | 18 +++++++++++++++--- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/ColorCode.Core/CodeColorizerBase.cs b/ColorCode.Core/CodeColorizerBase.cs index 2618b58..308fa27 100644 --- a/ColorCode.Core/CodeColorizerBase.cs +++ b/ColorCode.Core/CodeColorizerBase.cs @@ -1,6 +1,7 @@ using ColorCode.Compilation; using ColorCode.Parsing; using ColorCode.Styling; +using System; using System.Collections.Generic; namespace ColorCode @@ -10,12 +11,13 @@ namespace ColorCode /// /// The Custom styles to Apply to the formatted Code. /// The language parser that the instance will use for its lifetime. + /// The default amount of time spent parsing before throwing a public abstract class CodeColorizerBase { - public CodeColorizerBase(StyleDictionary Styles, ILanguageParser languageParser) + public CodeColorizerBase(StyleDictionary Styles, ILanguageParser languageParser, double defaultParseTimeoutSec = 4) { this.languageParser = languageParser - ?? new LanguageParser(new LanguageCompiler(Languages.CompiledLanguages), Languages.LanguageRepository); + ?? new LanguageParser(new LanguageCompiler(Languages.CompiledLanguages), Languages.LanguageRepository, defaultParseTimeoutSec); this.Styles = Styles ?? StyleDictionary.DefaultLight; } diff --git a/ColorCode.Core/Compilation/ILanguageCompiler.cs b/ColorCode.Core/Compilation/ILanguageCompiler.cs index 97e696c..dfda630 100644 --- a/ColorCode.Core/Compilation/ILanguageCompiler.cs +++ b/ColorCode.Core/Compilation/ILanguageCompiler.cs @@ -4,6 +4,6 @@ namespace ColorCode.Compilation { public interface ILanguageCompiler { - CompiledLanguage Compile(ILanguage language); + CompiledLanguage Compile(ILanguage language, double defaultParseTimeoutSec); } } \ No newline at end of file diff --git a/ColorCode.Core/Compilation/LanguageCompiler.cs b/ColorCode.Core/Compilation/LanguageCompiler.cs index 92266df..106ba2f 100644 --- a/ColorCode.Core/Compilation/LanguageCompiler.cs +++ b/ColorCode.Core/Compilation/LanguageCompiler.cs @@ -12,7 +12,6 @@ namespace ColorCode.Compilation public class LanguageCompiler : ILanguageCompiler { private static readonly Regex numberOfCapturesRegex = new Regex(@"(?x)(? compiledLanguages; private readonly ReaderWriterLockSlim compileLock; @@ -23,7 +22,7 @@ public LanguageCompiler(Dictionary compiledLanguages) compileLock = new ReaderWriterLockSlim(); } - public CompiledLanguage Compile(ILanguage language) + public CompiledLanguage Compile(ILanguage language, double defaultParseTimeoutSec) { Guard.ArgNotNull(language, "language"); @@ -63,7 +62,7 @@ public CompiledLanguage Compile(ILanguage language) if (language.Rules == null || language.Rules.Count == 0) throw new ArgumentException("The language rules collection must not be empty.", "language"); - compiledLanguage = CompileLanguage(language); + compiledLanguage = CompileLanguage(language, defaultParseTimeoutSec); compiledLanguages.Add(compiledLanguage.Id, compiledLanguage); } @@ -81,17 +80,17 @@ public CompiledLanguage Compile(ILanguage language) return compiledLanguage; } - private static CompiledLanguage CompileLanguage(ILanguage language) + private static CompiledLanguage CompileLanguage(ILanguage language, double defaultParseTimeoutSec) { string id = language.Id; string name = language.Name; - CompileRules(language.Rules, out Regex regex, out IList captures); + CompileRules(language.Rules, defaultParseTimeoutSec, out Regex regex, out IList captures); return new CompiledLanguage(id, name, regex, captures); } - private static void CompileRules(IList rules, out Regex regex, out IList captures) + private static void CompileRules(IList rules, double defaultParseTimeoutSec, out Regex regex, out IList captures) { StringBuilder regexBuilder = new StringBuilder(); captures = new List(); @@ -104,7 +103,7 @@ private static void CompileRules(IList rules, out Regex regex, out for (int i = 1; i < rules.Count; i++) CompileRule(rules[i], regexBuilder, captures, false); - regex = new Regex(regexBuilder.ToString(), RegexOptions.None, defaultRegexParseTimeout); + regex = new Regex(regexBuilder.ToString(), RegexOptions.None, TimeSpan.FromSeconds(defaultParseTimeoutSec)); } private static void CompileRule(LanguageRule languageRule, StringBuilder regex, ICollection captures, bool isFirstRule) diff --git a/ColorCode.Core/Parsing/LanguageParser.cs b/ColorCode.Core/Parsing/LanguageParser.cs index 9b76033..dddfb4b 100644 --- a/ColorCode.Core/Parsing/LanguageParser.cs +++ b/ColorCode.Core/Parsing/LanguageParser.cs @@ -12,14 +12,26 @@ public class LanguageParser : ILanguageParser { private readonly ILanguageCompiler languageCompiler; private readonly ILanguageRepository languageRepository; + private readonly double defaultParseTimeoutSec = 1; public LanguageParser(ILanguageCompiler languageCompiler, - ILanguageRepository languageRepository) + ILanguageRepository languageRepository, + double defaultParseTimeoutSec) { this.languageCompiler = languageCompiler; this.languageRepository = languageRepository; + this.defaultParseTimeoutSec = defaultParseTimeoutSec; } + /// + /// Formats the specified text using the rules defined by the specified language. + /// + /// The text to parse + /// The language used to define the syntax highlighting rules + /// Called after parsing is complete + /// + /// Thrown if parsing takes longer than the default specified duration + /// public void Parse(string sourceCode, ILanguage language, Action> parseHandler) @@ -27,7 +39,7 @@ public void Parse(string sourceCode, if (string.IsNullOrEmpty(sourceCode)) return; - CompiledLanguage compiledLanguage = languageCompiler.Compile(language); + CompiledLanguage compiledLanguage = languageCompiler.Compile(language, defaultParseTimeoutSec); Parse(sourceCode, compiledLanguage, parseHandler); } @@ -157,7 +169,7 @@ private void AppendCapturedStylesForNestedLanguage(Capture regexCapture, throw new InvalidOperationException("The nested language was not found in the language repository."); else { - CompiledLanguage nestedCompiledLanguage = languageCompiler.Compile(nestedLanguage); + CompiledLanguage nestedCompiledLanguage = languageCompiler.Compile(nestedLanguage, defaultParseTimeoutSec); Match regexMatch = nestedCompiledLanguage.Regex.Match(regexCapture.Value, 0, regexCapture.Value.Length);