templater: abort if infinite recursion detected while compiling
In this case, a template is parsed recursively with no thunk for lazy
evaluation. This patch prevents recursion by putting a dummy of the same name
into a cache that will be referenced while parsing if there's a recursion.
changeset = {files % changeset}\n
~~~~~~~~~
= [(_runrecursivesymbol, 'changeset')]
--- a/mercurial/templater.py Wed Jul 22 23:29:41 2015 +0900
+++ b/mercurial/templater.py Thu Jul 23 23:41:29 2015 +0900
@@ -231,6 +231,9 @@
raise error.Abort(_("recursive reference '%s' in template") % key)
return showrecursion
+def _runrecursivesymbol(context, mapping, key):
+ raise error.Abort(_("recursive reference '%s' in template") % key)
+
def runsymbol(context, mapping, key):
v = mapping.get(key)
if v is None:
@@ -826,7 +829,13 @@
def _load(self, t):
'''load, parse, and cache a template'''
if t not in self._cache:
- self._cache[t] = compiletemplate(self._loader(t), self)
+ # put poison to cut recursion while compiling 't'
+ self._cache[t] = [(_runrecursivesymbol, t)]
+ try:
+ self._cache[t] = compiletemplate(self._loader(t), self)
+ except: # re-raises
+ del self._cache[t]
+ raise
return self._cache[t]
def process(self, t, mapping):
--- a/tests/test-command-template.t Wed Jul 22 23:29:41 2015 +0900
+++ b/tests/test-command-template.t Thu Jul 23 23:41:29 2015 +0900
@@ -1053,6 +1053,12 @@
abort: recursive reference 'foo' in template
[255]
+ buildmap() -> gettemplate(), where no thunk was made:
+
+ $ hg log -T '{files % changeset}\n'
+ abort: recursive reference 'changeset' in template
+ [255]
+
not a recursion if a keyword of the same name exists:
$ cat << EOF > issue4758