Mercurial > public > mercurial-scm > hg
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 |