From 84fe0a384d78261aac9629f7bdca9e86b39b19e2 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Fri, 22 Mar 2013 10:38:48 -0700 Subject: [PATCH] Pop grammar rules that result in infinite loops --- spec/app/text-mate-grammar-spec.coffee | 10 ++++++++++ .../grammars/grammar.cson | 18 ++++++++++++++++++ src/app/text-mate-grammar.coffee | 7 ++++++- 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 spec/fixtures/packages/package-with-infinite-loop-grammar/grammars/grammar.cson diff --git a/spec/app/text-mate-grammar-spec.coffee b/spec/app/text-mate-grammar-spec.coffee index d3834b543..aee8b00a3 100644 --- a/spec/app/text-mate-grammar-spec.coffee +++ b/spec/app/text-mate-grammar-spec.coffee @@ -258,3 +258,13 @@ describe "TextMateGrammar", -> {tokens, ruleStack} = grammar.tokenizeLine("if(1){if(1){m()}}") expect(tokens[5]).toEqual value: "if", scopes: ["source.c", "meta.block.c", "keyword.control.c"] expect(tokens[10]).toEqual value: "m", scopes: ["source.c", "meta.block.c", "meta.block.c", "meta.function-call.c", "support.function.any-method.c"] + + describe "when the grammar can infinitely loop over a line", -> + it "aborts tokenization", -> + spyOn(console, 'error') + window.loadPackage("package-with-infinite-loop-grammar") + grammar = syntax.grammarForFilePath("something.package-with-infinite-loop-grammar") + {tokens} = grammar.tokenizeLine("abc") + expect(tokens[0].value).toBe "a" + expect(tokens[1].value).toBe "bc" + expect(console.error).toHaveBeenCalled() diff --git a/spec/fixtures/packages/package-with-infinite-loop-grammar/grammars/grammar.cson b/spec/fixtures/packages/package-with-infinite-loop-grammar/grammars/grammar.cson new file mode 100644 index 000000000..8ac183c23 --- /dev/null +++ b/spec/fixtures/packages/package-with-infinite-loop-grammar/grammars/grammar.cson @@ -0,0 +1,18 @@ +'fileTypes': ['package-with-infinite-loop-grammar'] +'name': 'package-with-infinite-loop-grammar' +'scopeName': 'source.package-with-infinite-loop-grammar' + +# This grammar should loop forever if the line contains an `a` +'patterns': [ + { + 'name': 'start' + 'begin': '^' + 'end': '$' + 'patterns': [ + { + name: 'negative-look-ahead' + match: "(?!a)" + } + ] + } +] diff --git a/src/app/text-mate-grammar.coffee b/src/app/text-mate-grammar.coffee index 0174edb10..22a83612e 100644 --- a/src/app/text-mate-grammar.coffee +++ b/src/app/text-mate-grammar.coffee @@ -31,9 +31,10 @@ class TextMateGrammar ruleStack = new Array(ruleStack...) # clone ruleStack tokens = [] position = 0 - loop scopes = scopesFromStack(ruleStack) + previousRuleStackLength = ruleStack.length + previousPosition = position if line.length == 0 tokens = [new Token(value: "", scopes: scopes)] @@ -61,6 +62,10 @@ class TextMateGrammar )) break + if position == previousPosition and ruleStack.length == previousRuleStackLength + console.error("Popping rule because it loops at column #{position} of line '#{line}'", _.clone(ruleStack)) + ruleStack.pop() + ruleStack.forEach (rule) -> rule.clearAnchorPosition() { tokens, ruleStack }