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')]
This commit is contained in:
Yuya Nishihara 2015-07-23 23:41:29 +09:00
parent 554208ae8f
commit 1f60c51fa5
2 changed files with 16 additions and 1 deletions

View File

@ -231,6 +231,9 @@ def _recursivesymbolblocker(key):
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 @@ class engine(object):
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):

View File

@ -1053,6 +1053,12 @@ Check that recursive reference does not fall into RuntimeError (issue4758):
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