diff options
Diffstat (limited to 'src/host/genminilua.lua')
-rw-r--r-- | src/host/genminilua.lua | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/src/host/genminilua.lua b/src/host/genminilua.lua new file mode 100644 index 00000000..7282cf16 --- /dev/null +++ b/src/host/genminilua.lua | |||
@@ -0,0 +1,363 @@ | |||
1 | ---------------------------------------------------------------------------- | ||
2 | -- Lua script to generate a customized, minified version of Lua. | ||
3 | -- The resulting 'minilua' is used for the build process of LuaJIT. | ||
4 | ---------------------------------------------------------------------------- | ||
5 | -- Copyright (C) 2005-2012 Mike Pall. All rights reserved. | ||
6 | -- Released under the MIT license. See Copyright Notice in luajit.h | ||
7 | ---------------------------------------------------------------------------- | ||
8 | |||
9 | local sub, match, gsub = string.sub, string.match, string.gsub | ||
10 | |||
11 | local LUA_VERSION = "5.1.5" | ||
12 | local LUA_SOURCE | ||
13 | |||
14 | local function usage() | ||
15 | io.stderr:write("Usage: ", arg and arg[0] or "genminilua", | ||
16 | " lua-", LUA_VERSION, "-source-dir\n") | ||
17 | os.exit(1) | ||
18 | end | ||
19 | |||
20 | local function find_sources() | ||
21 | LUA_SOURCE = arg and arg[1] | ||
22 | if not LUA_SOURCE then usage() end | ||
23 | if sub(LUA_SOURCE, -1) ~= "/" then LUA_SOURCE = LUA_SOURCE.."/" end | ||
24 | local fp = io.open(LUA_SOURCE .. "lua.h") | ||
25 | if not fp then | ||
26 | LUA_SOURCE = LUA_SOURCE.."src/" | ||
27 | fp = io.open(LUA_SOURCE .. "lua.h") | ||
28 | if not fp then usage() end | ||
29 | end | ||
30 | local all = fp:read("*a") | ||
31 | fp:close() | ||
32 | if not match(all, 'LUA_RELEASE%s*"Lua '..LUA_VERSION..'"') then | ||
33 | io.stderr:write("Error: version mismatch\n") | ||
34 | usage() | ||
35 | end | ||
36 | end | ||
37 | |||
38 | local LUA_FILES = { | ||
39 | "lmem.c", "lobject.c", "ltm.c", "lfunc.c", "ldo.c", "lstring.c", "ltable.c", | ||
40 | "lgc.c", "lstate.c", "ldebug.c", "lzio.c", "lopcodes.c", | ||
41 | "llex.c", "lcode.c", "lparser.c", "lvm.c", "lapi.c", "lauxlib.c", | ||
42 | "lbaselib.c", "ltablib.c", "liolib.c", "loslib.c", "lstrlib.c", "linit.c", | ||
43 | } | ||
44 | |||
45 | local REMOVE_LIB = {} | ||
46 | gsub([[ | ||
47 | collectgarbage dofile gcinfo getfenv getmetatable load print rawequal rawset | ||
48 | select tostring xpcall | ||
49 | foreach foreachi getn maxn setn | ||
50 | popen tmpfile seek setvbuf __tostring | ||
51 | clock date difftime execute getenv rename setlocale time tmpname | ||
52 | dump gfind len reverse | ||
53 | LUA_LOADLIBNAME LUA_MATHLIBNAME LUA_DBLIBNAME | ||
54 | ]], "%S+", function(name) | ||
55 | REMOVE_LIB[name] = true | ||
56 | end) | ||
57 | |||
58 | local REMOVE_EXTINC = { ["<assert.h>"] = true, ["<locale.h>"] = true, } | ||
59 | |||
60 | local CUSTOM_MAIN = [[ | ||
61 | int main(int argc, char **argv){ | ||
62 | lua_State *L = luaL_newstate(); | ||
63 | int i; | ||
64 | luaL_openlibs(L); | ||
65 | if (argc < 2) return 1; | ||
66 | lua_createtable(L, 0, 1); | ||
67 | lua_pushstring(L, argv[1]); | ||
68 | lua_rawseti(L, -2, 0); | ||
69 | lua_setglobal(L, "arg"); | ||
70 | if (luaL_loadfile(L, argv[1])) | ||
71 | goto err; | ||
72 | for (i = 2; i < argc; i++) | ||
73 | lua_pushstring(L, argv[i]); | ||
74 | if (lua_pcall(L, argc - 2, 0, 0)) { | ||
75 | err: | ||
76 | fprintf(stderr, "Error: %s\n", lua_tostring(L, -1)); | ||
77 | return 1; | ||
78 | } | ||
79 | lua_close(L); | ||
80 | return 0; | ||
81 | } | ||
82 | ]] | ||
83 | |||
84 | local function read_sources() | ||
85 | local t = {} | ||
86 | for i, name in ipairs(LUA_FILES) do | ||
87 | local fp = assert(io.open(LUA_SOURCE..name, "r")) | ||
88 | t[i] = fp:read("*a") | ||
89 | assert(fp:close()) | ||
90 | end | ||
91 | t[#t+1] = CUSTOM_MAIN | ||
92 | return table.concat(t) | ||
93 | end | ||
94 | |||
95 | local includes = {} | ||
96 | |||
97 | local function merge_includes(src) | ||
98 | return gsub(src, '#include%s*"([^"]*)"%s*\n', function(name) | ||
99 | if includes[name] then return "" end | ||
100 | includes[name] = true | ||
101 | local fp = assert(io.open(LUA_SOURCE..name, "r")) | ||
102 | local src = fp:read("*a") | ||
103 | assert(fp:close()) | ||
104 | src = gsub(src, "#ifndef%s+%w+_h\n#define%s+%w+_h\n", "") | ||
105 | src = gsub(src, "#endif%s*$", "") | ||
106 | return merge_includes(src) | ||
107 | end) | ||
108 | end | ||
109 | |||
110 | local function get_license(src) | ||
111 | return match(src, "/%*+\n%* Copyright %(.-%*/\n") | ||
112 | end | ||
113 | |||
114 | local function fold_lines(src) | ||
115 | return gsub(src, "\\\n", " ") | ||
116 | end | ||
117 | |||
118 | local strings = {} | ||
119 | |||
120 | local function save_str(str) | ||
121 | local n = #strings+1 | ||
122 | strings[n] = str | ||
123 | return "\1"..n.."\2" | ||
124 | end | ||
125 | |||
126 | local function save_strings(src) | ||
127 | src = gsub(src, '"[^"\n]*"', save_str) | ||
128 | return gsub(src, "'[^'\n]*'", save_str) | ||
129 | end | ||
130 | |||
131 | local function restore_strings(src) | ||
132 | return gsub(src, "\1(%d+)\2", function(numstr) | ||
133 | return strings[tonumber(numstr)] | ||
134 | end) | ||
135 | end | ||
136 | |||
137 | local function def_istrue(def) | ||
138 | return def == "INT_MAX > 2147483640L" or | ||
139 | def == "LUAI_BITSINT >= 32" or | ||
140 | def == "SIZE_Bx < LUAI_BITSINT-1" or | ||
141 | def == "cast" or | ||
142 | def == "defined(LUA_CORE)" or | ||
143 | def == "MINSTRTABSIZE" or | ||
144 | def == "LUA_MINBUFFER" or | ||
145 | def == "HARDSTACKTESTS" or | ||
146 | def == "UNUSED" | ||
147 | end | ||
148 | |||
149 | local head, defs = {}, {} | ||
150 | |||
151 | local function preprocess(src) | ||
152 | local t = { match(src, "^(.-)#") } | ||
153 | local lvl, on, oldon = 0, true, {} | ||
154 | for pp, def, txt in string.gmatch(src, "#(%w+) *([^\n]*)\n([^#]*)") do | ||
155 | if pp == "if" or pp == "ifdef" or pp == "ifndef" then | ||
156 | lvl = lvl + 1 | ||
157 | oldon[lvl] = on | ||
158 | on = def_istrue(def) | ||
159 | elseif pp == "else" then | ||
160 | if oldon[lvl] then | ||
161 | if on == false then on = true else on = false end | ||
162 | end | ||
163 | elseif pp == "elif" then | ||
164 | if oldon[lvl] then | ||
165 | on = def_istrue(def) | ||
166 | end | ||
167 | elseif pp == "endif" then | ||
168 | on = oldon[lvl] | ||
169 | lvl = lvl - 1 | ||
170 | elseif on then | ||
171 | if pp == "include" then | ||
172 | if not head[def] and not REMOVE_EXTINC[def] then | ||
173 | head[def] = true | ||
174 | head[#head+1] = "#include "..def.."\n" | ||
175 | end | ||
176 | elseif pp == "define" then | ||
177 | local k, sp, v = match(def, "([%w_]+)(%s*)(.*)") | ||
178 | if k and not (sp == "" and sub(v, 1, 1) == "(") then | ||
179 | defs[k] = gsub(v, "%a[%w_]*", function(tok) | ||
180 | return defs[tok] or tok | ||
181 | end) | ||
182 | else | ||
183 | t[#t+1] = "#define "..def.."\n" | ||
184 | end | ||
185 | elseif pp ~= "undef" then | ||
186 | error("unexpected directive: "..pp.." "..def) | ||
187 | end | ||
188 | end | ||
189 | if on then t[#t+1] = txt end | ||
190 | end | ||
191 | return gsub(table.concat(t), "%a[%w_]*", function(tok) | ||
192 | return defs[tok] or tok | ||
193 | end) | ||
194 | end | ||
195 | |||
196 | local function merge_header(src, license) | ||
197 | local hdr = string.format([[ | ||
198 | /* This is a heavily customized and minimized copy of Lua %s. */ | ||
199 | /* It's only used to build LuaJIT. It does NOT have all standard functions! */ | ||
200 | ]], LUA_VERSION) | ||
201 | return hdr..license..table.concat(head)..src | ||
202 | end | ||
203 | |||
204 | local function strip_unused1(src) | ||
205 | return gsub(src, '( {"?([%w_]+)"?,%s+%a[%w_]*},\n)', function(line, func) | ||
206 | return REMOVE_LIB[func] and "" or line | ||
207 | end) | ||
208 | end | ||
209 | |||
210 | local function strip_unused2(src) | ||
211 | return gsub(src, "Symbolic Execution.-}=", "") | ||
212 | end | ||
213 | |||
214 | local function strip_unused3(src) | ||
215 | src = gsub(src, "extern", "static") | ||
216 | src = gsub(src, "\nstatic([^\n]-)%(([^)]*)%)%(", "\nstatic%1 %2(") | ||
217 | src = gsub(src, "#define lua_assert[^\n]*\n", "") | ||
218 | src = gsub(src, "lua_assert%b();?", "") | ||
219 | src = gsub(src, "default:\n}", "default:;\n}") | ||
220 | src = gsub(src, "lua_lock%b();", "") | ||
221 | src = gsub(src, "lua_unlock%b();", "") | ||
222 | src = gsub(src, "luai_threadyield%b();", "") | ||
223 | src = gsub(src, "luai_userstateopen%b();", "{}") | ||
224 | src = gsub(src, "luai_userstate%w+%b();", "") | ||
225 | src = gsub(src, "%(%(c==.*luaY_parser%)", "luaY_parser") | ||
226 | src = gsub(src, "trydecpoint%(ls,seminfo%)", | ||
227 | "luaX_lexerror(ls,\"malformed number\",TK_NUMBER)") | ||
228 | src = gsub(src, "int c=luaZ_lookahead%b();", "") | ||
229 | src = gsub(src, "luaL_register%(L,\"coroutine\",co_funcs%);\nreturn 2;", | ||
230 | "return 1;") | ||
231 | src = gsub(src, "getfuncname%b():", "NULL:") | ||
232 | src = gsub(src, "getobjname%b():", "NULL:") | ||
233 | src = gsub(src, "if%([^\n]*hookmask[^\n]*%)\n[^\n]*\n", "") | ||
234 | src = gsub(src, "if%([^\n]*hookmask[^\n]*%)%b{}\n", "") | ||
235 | src = gsub(src, "if%([^\n]*hookmask[^\n]*&&\n[^\n]*%b{}\n", "") | ||
236 | src = gsub(src, "(twoto%b()%()", "%1(size_t)") | ||
237 | src = gsub(src, "i<sizenode", "i<(int)sizenode") | ||
238 | return gsub(src, "\n\n+", "\n") | ||
239 | end | ||
240 | |||
241 | local function strip_comments(src) | ||
242 | return gsub(src, "/%*.-%*/", " ") | ||
243 | end | ||
244 | |||
245 | local function strip_whitespace(src) | ||
246 | src = gsub(src, "^%s+", "") | ||
247 | src = gsub(src, "%s*\n%s*", "\n") | ||
248 | src = gsub(src, "[ \t]+", " ") | ||
249 | src = gsub(src, "(%W) ", "%1") | ||
250 | return gsub(src, " (%W)", "%1") | ||
251 | end | ||
252 | |||
253 | local function rename_tokens1(src) | ||
254 | src = gsub(src, "getline", "getline_") | ||
255 | src = gsub(src, "struct ([%w_]+)", "ZX%1") | ||
256 | return gsub(src, "union ([%w_]+)", "ZY%1") | ||
257 | end | ||
258 | |||
259 | local function rename_tokens2(src) | ||
260 | src = gsub(src, "ZX([%w_]+)", "struct %1") | ||
261 | return gsub(src, "ZY([%w_]+)", "union %1") | ||
262 | end | ||
263 | |||
264 | local function func_gather(src) | ||
265 | local nodes, list = {}, {} | ||
266 | local pos, len = 1, #src | ||
267 | while pos < len do | ||
268 | local d, w = match(src, "^(#define ([%w_]+)[^\n]*\n)", pos) | ||
269 | if d then | ||
270 | local n = #list+1 | ||
271 | list[n] = d | ||
272 | nodes[w] = n | ||
273 | else | ||
274 | local s | ||
275 | d, w, s = match(src, "^(([%w_]+)[^\n]*([{;])\n)", pos) | ||
276 | if not d then | ||
277 | d, w, s = match(src, "^(([%w_]+)[^(]*%b()([{;])\n)", pos) | ||
278 | if not d then d = match(src, "^[^\n]*\n", pos) end | ||
279 | end | ||
280 | if s == "{" then | ||
281 | d = d..sub(match(src, "^%b{}[^;\n]*;?\n", pos+#d-2), 3) | ||
282 | if sub(d, -2) == "{\n" then | ||
283 | d = d..sub(match(src, "^%b{}[^;\n]*;?\n", pos+#d-2), 3) | ||
284 | end | ||
285 | end | ||
286 | local k, v = nil, d | ||
287 | if w == "typedef" then | ||
288 | if match(d, "^typedef enum") then | ||
289 | head[#head+1] = d | ||
290 | else | ||
291 | k = match(d, "([%w_]+);\n$") | ||
292 | if not k then k = match(d, "^.-%(.-([%w_]+)%)%(") end | ||
293 | end | ||
294 | elseif w == "enum" then | ||
295 | head[#head+1] = v | ||
296 | elseif w ~= nil then | ||
297 | k = match(d, "^[^\n]-([%w_]+)[(%[=]") | ||
298 | if k then | ||
299 | if w ~= "static" and k ~= "main" then v = "static "..d end | ||
300 | else | ||
301 | k = w | ||
302 | end | ||
303 | end | ||
304 | if w and k then | ||
305 | local o = nodes[k] | ||
306 | if o then nodes["*"..k] = o end | ||
307 | local n = #list+1 | ||
308 | list[n] = v | ||
309 | nodes[k] = n | ||
310 | end | ||
311 | end | ||
312 | pos = pos + #d | ||
313 | end | ||
314 | return nodes, list | ||
315 | end | ||
316 | |||
317 | local function func_visit(nodes, list, used, n) | ||
318 | local i = nodes[n] | ||
319 | for m in string.gmatch(list[i], "[%w_]+") do | ||
320 | if nodes[m] then | ||
321 | local j = used[m] | ||
322 | if not j then | ||
323 | used[m] = i | ||
324 | func_visit(nodes, list, used, m) | ||
325 | elseif i < j then | ||
326 | used[m] = i | ||
327 | end | ||
328 | end | ||
329 | end | ||
330 | end | ||
331 | |||
332 | local function func_collect(src) | ||
333 | local nodes, list = func_gather(src) | ||
334 | local used = {} | ||
335 | func_visit(nodes, list, used, "main") | ||
336 | for n,i in pairs(nodes) do | ||
337 | local j = used[n] | ||
338 | if j and j < i then used["*"..n] = j end | ||
339 | end | ||
340 | for n,i in pairs(nodes) do | ||
341 | if not used[n] then list[i] = "" end | ||
342 | end | ||
343 | return table.concat(list) | ||
344 | end | ||
345 | |||
346 | find_sources() | ||
347 | local src = read_sources() | ||
348 | src = merge_includes(src) | ||
349 | local license = get_license(src) | ||
350 | src = fold_lines(src) | ||
351 | src = strip_unused1(src) | ||
352 | src = save_strings(src) | ||
353 | src = strip_unused2(src) | ||
354 | src = strip_comments(src) | ||
355 | src = preprocess(src) | ||
356 | src = strip_whitespace(src) | ||
357 | src = strip_unused3(src) | ||
358 | src = rename_tokens1(src) | ||
359 | src = func_collect(src) | ||
360 | src = rename_tokens2(src) | ||
361 | src = restore_strings(src) | ||
362 | src = merge_header(src, license) | ||
363 | io.write(src) | ||