aboutsummaryrefslogtreecommitdiff
path: root/src/host/genminilua.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/host/genminilua.lua')
-rw-r--r--src/host/genminilua.lua363
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
9local sub, match, gsub = string.sub, string.match, string.gsub
10
11local LUA_VERSION = "5.1.5"
12local LUA_SOURCE
13
14local function usage()
15 io.stderr:write("Usage: ", arg and arg[0] or "genminilua",
16 " lua-", LUA_VERSION, "-source-dir\n")
17 os.exit(1)
18end
19
20local 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
36end
37
38local 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
45local REMOVE_LIB = {}
46gsub([[
47collectgarbage dofile gcinfo getfenv getmetatable load print rawequal rawset
48select tostring xpcall
49foreach foreachi getn maxn setn
50popen tmpfile seek setvbuf __tostring
51clock date difftime execute getenv rename setlocale time tmpname
52dump gfind len reverse
53LUA_LOADLIBNAME LUA_MATHLIBNAME LUA_DBLIBNAME
54]], "%S+", function(name)
55 REMOVE_LIB[name] = true
56end)
57
58local REMOVE_EXTINC = { ["<assert.h>"] = true, ["<locale.h>"] = true, }
59
60local CUSTOM_MAIN = [[
61int 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
84local 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)
93end
94
95local includes = {}
96
97local 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)
108end
109
110local function get_license(src)
111 return match(src, "/%*+\n%* Copyright %(.-%*/\n")
112end
113
114local function fold_lines(src)
115 return gsub(src, "\\\n", " ")
116end
117
118local strings = {}
119
120local function save_str(str)
121 local n = #strings+1
122 strings[n] = str
123 return "\1"..n.."\2"
124end
125
126local function save_strings(src)
127 src = gsub(src, '"[^"\n]*"', save_str)
128 return gsub(src, "'[^'\n]*'", save_str)
129end
130
131local function restore_strings(src)
132 return gsub(src, "\1(%d+)\2", function(numstr)
133 return strings[tonumber(numstr)]
134 end)
135end
136
137local 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"
147end
148
149local head, defs = {}, {}
150
151local 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)
194end
195
196local 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
202end
203
204local 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)
208end
209
210local function strip_unused2(src)
211 return gsub(src, "Symbolic Execution.-}=", "")
212end
213
214local 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")
239end
240
241local function strip_comments(src)
242 return gsub(src, "/%*.-%*/", " ")
243end
244
245local 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")
251end
252
253local 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")
257end
258
259local function rename_tokens2(src)
260 src = gsub(src, "ZX([%w_]+)", "struct %1")
261 return gsub(src, "ZY([%w_]+)", "union %1")
262end
263
264local 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
315end
316
317local 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
330end
331
332local 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)
344end
345
346find_sources()
347local src = read_sources()
348src = merge_includes(src)
349local license = get_license(src)
350src = fold_lines(src)
351src = strip_unused1(src)
352src = save_strings(src)
353src = strip_unused2(src)
354src = strip_comments(src)
355src = preprocess(src)
356src = strip_whitespace(src)
357src = strip_unused3(src)
358src = rename_tokens1(src)
359src = func_collect(src)
360src = rename_tokens2(src)
361src = restore_strings(src)
362src = merge_header(src, license)
363io.write(src)