comparison mercurial/revset.py @ 27987:b19d8d5d6b51 stable

revset: flatten chained 'list' operations (aka function args) (issue5072) Internal _matchfiles() function can take bunch of arguments, which would lead to a maximum recursion depth error. This patch avoids the excessive stack use by flattening 'list' nodes beforehand. Since getlist() no longer takes a nested 'list' nodes, _parsealiasdecl() also needs to flatten argument list, "aliasname($1, $2, ...)".
author Yuya Nishihara <yuya@tcha.org>
date Tue, 02 Feb 2016 23:49:49 +0900
parents 4186d359046a
children a036e1ae1fbe
comparison
equal deleted inserted replaced
27986:a58378983687 27987:b19d8d5d6b51
317 317
318 def getlist(x): 318 def getlist(x):
319 if not x: 319 if not x:
320 return [] 320 return []
321 if x[0] == 'list': 321 if x[0] == 'list':
322 return getlist(x[1]) + [x[2]] 322 return list(x[1:])
323 return [x] 323 return [x]
324 324
325 def getargs(x, min, max, err): 325 def getargs(x, min, max, err):
326 l = getlist(x) 326 l = getlist(x)
327 if len(l) < min or (max >= 0 and len(l) > max): 327 if len(l) < min or (max >= 0 and len(l) > max):
446 return a + b 446 return a + b
447 447
448 def notset(repo, subset, x): 448 def notset(repo, subset, x):
449 return subset - getset(repo, subset, x) 449 return subset - getset(repo, subset, x)
450 450
451 def listset(repo, subset, a, b): 451 def listset(repo, subset, *xs):
452 raise error.ParseError(_("can't use a list in this context"), 452 raise error.ParseError(_("can't use a list in this context"),
453 hint=_('see hg help "revsets.x or y"')) 453 hint=_('see hg help "revsets.x or y"'))
454 454
455 def keyvaluepair(repo, subset, k, v): 455 def keyvaluepair(repo, subset, k, v):
456 raise error.ParseError(_("can't use a key-value pair in this context")) 456 raise error.ParseError(_("can't use a key-value pair in this context"))
2250 elif op == 'parentpost': 2250 elif op == 'parentpost':
2251 o = optimize(x[1], small) 2251 o = optimize(x[1], small)
2252 return o[0], (op, o[1]) 2252 return o[0], (op, o[1])
2253 elif op == 'group': 2253 elif op == 'group':
2254 return optimize(x[1], small) 2254 return optimize(x[1], small)
2255 elif op in 'dagrange range list parent ancestorspec': 2255 elif op in 'dagrange range parent ancestorspec':
2256 if op == 'parent': 2256 if op == 'parent':
2257 # x^:y means (x^) : y, not x ^ (:y) 2257 # x^:y means (x^) : y, not x ^ (:y)
2258 post = ('parentpost', x[1]) 2258 post = ('parentpost', x[1])
2259 if x[2][0] == 'dagrangepre': 2259 if x[2][0] == 'dagrangepre':
2260 return optimize(('dagrange', post, x[2][1]), small) 2260 return optimize(('dagrange', post, x[2][1]), small)
2262 return optimize(('range', post, x[2][1]), small) 2262 return optimize(('range', post, x[2][1]), small)
2263 2263
2264 wa, ta = optimize(x[1], small) 2264 wa, ta = optimize(x[1], small)
2265 wb, tb = optimize(x[2], small) 2265 wb, tb = optimize(x[2], small)
2266 return wa + wb, (op, ta, tb) 2266 return wa + wb, (op, ta, tb)
2267 elif op == 'list':
2268 ws, ts = zip(*(optimize(y, small) for y in x[1:]))
2269 return sum(ws), (op,) + ts
2267 elif op == 'func': 2270 elif op == 'func':
2268 f = getstring(x[1], _("not a symbol")) 2271 f = getstring(x[1], _("not a symbol"))
2269 wa, ta = optimize(x[2], small) 2272 wa, ta = optimize(x[2], small)
2270 if f in ("author branch closed date desc file grep keyword " 2273 if f in ("author branch closed date desc file grep keyword "
2271 "outgoing user"): 2274 "outgoing user"):
2363 p = parser.parser(elements) 2366 p = parser.parser(elements)
2364 try: 2367 try:
2365 tree, pos = p.parse(_tokenizealias(decl)) 2368 tree, pos = p.parse(_tokenizealias(decl))
2366 if (pos != len(decl)): 2369 if (pos != len(decl)):
2367 raise error.ParseError(_('invalid token'), pos) 2370 raise error.ParseError(_('invalid token'), pos)
2371 tree = parser.simplifyinfixops(tree, ('list',))
2368 2372
2369 if isvalidsymbol(tree): 2373 if isvalidsymbol(tree):
2370 # "name = ...." style 2374 # "name = ...." style
2371 name = getsymbol(tree) 2375 name = getsymbol(tree)
2372 if name.startswith('$'): 2376 if name.startswith('$'):
2453 2457
2454 p = parser.parser(elements) 2458 p = parser.parser(elements)
2455 tree, pos = p.parse(tokenizedefn(defn)) 2459 tree, pos = p.parse(tokenizedefn(defn))
2456 if pos != len(defn): 2460 if pos != len(defn):
2457 raise error.ParseError(_('invalid token'), pos) 2461 raise error.ParseError(_('invalid token'), pos)
2458 return parser.simplifyinfixops(tree, ('or',)) 2462 return parser.simplifyinfixops(tree, ('list', 'or'))
2459 2463
2460 class revsetalias(object): 2464 class revsetalias(object):
2461 # whether own `error` information is already shown or not. 2465 # whether own `error` information is already shown or not.
2462 # this avoids showing same warning multiple times at each `findaliases`. 2466 # this avoids showing same warning multiple times at each `findaliases`.
2463 warned = False 2467 warned = False
2584 def parse(spec, lookup=None): 2588 def parse(spec, lookup=None):
2585 p = parser.parser(elements) 2589 p = parser.parser(elements)
2586 tree, pos = p.parse(tokenize(spec, lookup=lookup)) 2590 tree, pos = p.parse(tokenize(spec, lookup=lookup))
2587 if pos != len(spec): 2591 if pos != len(spec):
2588 raise error.ParseError(_("invalid token"), pos) 2592 raise error.ParseError(_("invalid token"), pos)
2589 return parser.simplifyinfixops(tree, ('or',)) 2593 return parser.simplifyinfixops(tree, ('list', 'or'))
2590 2594
2591 def posttreebuilthook(tree, repo): 2595 def posttreebuilthook(tree, repo):
2592 # hook for extensions to execute code on the optimized tree 2596 # hook for extensions to execute code on the optimized tree
2593 pass 2597 pass
2594 2598