diff options
author | Alon Zakai <alonzakai@gmail.com> | 2012-01-10 17:54:59 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2012-01-10 17:54:59 -0800 |
commit | 41e3ba8e82863a75d3b81fdad9ebef319a4ac652 (patch) | |
tree | 59dade1777786a4f99a8f05ffeeb44520184f714 /tools/fix_closure.py | |
parent | 0a71c5438c46d535cc714b927ae328563d76eba6 (diff) |
experimental tool to fix closure's over-inlining into FUNCTION_TABLE
Diffstat (limited to 'tools/fix_closure.py')
-rwxr-xr-x | tools/fix_closure.py | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/tools/fix_closure.py b/tools/fix_closure.py new file mode 100755 index 00000000..c1d16cb7 --- /dev/null +++ b/tools/fix_closure.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +''' +With very very large projects, closure compiler can translate FUNCTION_TABLE into something like + + J = [0, 0, func, 0, f(), 0, function() { ... }, 0, ..] + +where f() returns a new empty function, and after it is an inlined function. This inlining can be of very very large functions, in which case it can make the source unparsable by any JS engine due to "too much recursion" or "Maximum call stack size exceeded". + +This script uninlines those functions. Note that we can't do this using Uglify, since these scripts can't be parsed by it either! +''' + +import os, sys, re + +infile = open(sys.argv[1], 'r') +outfile = open(sys.argv[2], 'w') + +class ObjectParser: + def read(self, s, line): + ''' + Read an element of the FUNCTION_TABLE until the end (a comma or the end of FUNCTION_TABLE), returning that location + ''' + assert line[s-1] == ',' # we are a new element, after a comma + curly = 0 + string = 0 + is_func = 0 + i = s + while True: + c = line[i] + if not string: + if c == '"' or c == "'": + string = 1 + elif c == '{': + is_func = 1 + curly += 1 + elif c == '}': + curly -= 1 + elif not curly: + if c in [',', ']']: + return (i, is_func and line[s:i].startswith('function(')) + else: + if c == '"' or c == "'": + string = 0 + i += 1 + +lines = infile.readlines() +i = 0 +while i < len(lines): + line = lines[i] + curr = line.find('=[0,0,') + if curr > 0: + # a suspect + target = line[curr-1] + curr += 5 + parser = ObjectParser() + add = [] + while line[curr] != ']': + assert line[curr] == ',' + curr += 1 + next, is_func = parser.read(curr, line) + if is_func: + text = line[curr:next] + assert text.startswith('function(') + ident = 'uninline_' + target + '_' + str(curr) # these idents should be unique, but might in theory collide with the rest of the JS code! XXX + line = line[:curr] + ident + line[next:] + add += 'function ' + ident + '(' + text[len('function('):] + while line[curr] != ',' and line[curr] != ']': + curr += 1 + lines[i] = line + lines = lines[:i] + add + lines[i:] + i += len(add) + i += 1 + +for line in lines: + outfile.write(line) +outfile.close() + |