1 ----------------------------------------------------------------------
3 -- WARNING! You're entering a hackish area, proceed at your own risks!
5 -- This code results from the borrowing, then ruthless abuse, of
6 -- Yueliang's implementation of Lua 5.0 compiler. I claim
7 -- responsibility for all of the ugly, dirty stuff that you might spot
10 -- Eventually, this code will be rewritten, either in Lua or more
11 -- probably in C. Meanwhile, if you're interested into digging
12 -- metalua's sources, this is not the best part to invest your time
17 ----------------------------------------------------------------------
19 --[[--------------------------------------------------------------------
21 $Id: ldump.lua,v 1.3 2006/11/07 04:38:00 fab13n Exp $
25 This file is part of Yueliang.
27 Copyright (c) 2005 Kein-Hong Man <khman@users.sf.net>
28 The COPYRIGHT file describes the conditions
29 under which this software may be distributed.
31 ------------------------------------------------------------------------
33 [FF] Slightly modified, mainly to produce Lua 5.1 bytecode.
36 Revision 1.3 2006/11/07 04:38:00 fab13n
37 first bootstrapping version.
39 Revision 1.2 2006/11/05 15:08:34 fab13n
40 updated code generation, to be compliant with 5.1
43 ----------------------------------------------------------------------]]
45 --[[--------------------------------------------------------------------
47 -- * LUA_NUMBER (double), byte order (little endian) and some other
48 -- header values hard-coded; see other notes below...
49 -- * One significant difference is that instructions are still in table
50 -- form (with OP/A/B/C/Bx fields) and luaP:Instruction() is needed to
51 -- convert them into 4-char strings
53 -- luaU:DumpVector: folded into DumpLines, DumpCode
55 -- luaU:endianness() (from lundump.c)
56 -- luaU:make_setS: create a chunk writer that writes to a string
57 -- luaU:make_setF: create a chunk writer that writes to a file
58 -- (lua.h contains a typedef for a Chunkwriter pointer, and
59 -- a Lua-based implementation exists, writer() in lstrlib.c)
60 -- luaU:from_double(x): encode double value for writing
61 -- luaU:from_int(x): encode integer value for writing
62 -- (error checking is limited for these conversion functions)
63 -- (double conversion does not support denormals or NaNs)
64 -- luaU:ttype(o) (from lobject.h)
65 ----------------------------------------------------------------------]]
67 module("bytecode", package.seeall)
72 -- constants used by dumper
75 luaU.LUA_TNUMBER = 3 -- (all in lua.h)
79 -- definitions for headers of binary files
80 luaU.LUA_SIGNATURE = "\27Lua" -- binary files start with "<esc>Lua"
81 luaU.VERSION = 81 -- 0x50; last format change was in 5.0
82 luaU.FORMAT_VERSION = 0 -- 0 is official version. yeah I know I'm a liar.
84 -- a multiple of PI for testing native format
85 -- multiplying by 1E7 gives non-trivial integer values
86 --luaU.TEST_NUMBER = 3.14159265358979323846E7
88 --[[--------------------------------------------------------------------
89 -- Additional functions to handle chunk writing
90 -- * to use make_setS and make_setF, see test_ldump.lua elsewhere
91 ----------------------------------------------------------------------]]
93 ------------------------------------------------------------------------
94 -- works like the lobject.h version except that TObject used in these
95 -- scripts only has a 'value' field, no 'tt' field (native types used)
96 ------------------------------------------------------------------------
97 function luaU:ttype(o)
98 local tt = type(o.value)
99 if tt == "number" then return self.LUA_TNUMBER
100 elseif tt == "string" then return self.LUA_TSTRING
101 elseif tt == "nil" then return self.LUA_TNIL
102 elseif tt == "boolean" then return self.LUA_TBOOLEAN
104 return self.LUA_TNONE -- the rest should not appear
108 ------------------------------------------------------------------------
109 -- create a chunk writer that writes to a string
110 -- * returns the writer function and a table containing the string
111 -- * to get the final result, look in buff.data
112 ------------------------------------------------------------------------
113 function luaU:make_setS()
117 function(s, buff) -- chunk writer
118 if not s then return end
119 buff.data = buff.data..s
124 ------------------------------------------------------------------------
125 -- create a chunk writer that writes to a file
126 -- * returns the writer function and a table containing the file handle
127 -- * if a nil is passed, then writer should close the open file
128 ------------------------------------------------------------------------
129 function luaU:make_setF(filename)
131 buff.h = io.open(filename, "wb")
132 if not buff.h then return nil end
134 function(s, buff) -- chunk writer
135 if not buff.h then return end
136 if not s then buff.h:close(); return end
142 -----------------------------------------------------------------------
143 -- converts a IEEE754 double number to an 8-byte little-endian string
144 -- * luaU:from_double() and luaU:from_int() are from ChunkBake project
145 -- * supports +/- Infinity, but not denormals or NaNs
146 -----------------------------------------------------------------------
147 function luaU:from_double(x)
148 local function grab_byte(v)
149 return math.floor(v / 256),
150 string.char(math.mod(math.floor(v), 256))
153 if x < 0 then sign = 1; x = -x end
154 local mantissa, exponent = math.frexp(x)
155 if x == 0 then -- zero
156 mantissa, exponent = 0, 0
158 mantissa, exponent = 0, 2047
160 mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53)
161 exponent = exponent + 1022
163 local v, byte = "" -- convert to bytes
166 x, byte = grab_byte(x); v = v..byte -- 47:0
168 x, byte = grab_byte(exponent * 16 + x); v = v..byte -- 55:48
169 x, byte = grab_byte(sign * 128 + x); v = v..byte -- 63:56
173 -----------------------------------------------------------------------
174 -- converts a number to a little-endian 32-bit integer string
175 -- * input value assumed to not overflow, can be signed/unsigned
176 -----------------------------------------------------------------------
177 function luaU:from_int(x)
182 v = v..string.char(math.mod(x, 256)); x = math.floor(x / 256)
188 local c = 255 - math.mod(x, 256) + carry
189 if c == 256 then c = 0; carry = 1 else carry = 0 end
190 v = v..string.char(c); x = math.floor(x / 256)
196 --[[--------------------------------------------------------------------
197 -- Functions to make a binary chunk
198 -- * many functions have the size parameter removed, since output is
199 -- in the form of a string and some sizes are implicit or hard-coded
200 -- * luaU:DumpVector has been deleted (used in DumpCode & DumpLines)
201 ----------------------------------------------------------------------]]
203 ------------------------------------------------------------------------
204 -- dump a block of literal bytes
205 ------------------------------------------------------------------------
206 function luaU:DumpLiteral(s, D) self:DumpBlock(s, D) end
208 --[[--------------------------------------------------------------------
210 -- L -- lua_State (not used in this script)
211 -- write -- lua_Chunkwriter (chunk writer function)
212 -- data -- void* (chunk writer context or data already written)
213 ----------------------------------------------------------------------]]
215 ------------------------------------------------------------------------
216 -- dumps a block of bytes
217 -- * lua_unlock(D.L), lua_lock(D.L) deleted
218 ------------------------------------------------------------------------
219 function luaU:DumpBlock(b, D) D.write(b, D.data) end
221 ------------------------------------------------------------------------
222 -- dumps a single byte
223 ------------------------------------------------------------------------
224 function luaU:DumpByte(y, D)
225 self:DumpBlock(string.char(y), D)
228 ------------------------------------------------------------------------
229 -- dumps a 32-bit signed integer (for int)
230 ------------------------------------------------------------------------
231 function luaU:DumpInt(x, D)
232 self:DumpBlock(self:from_int(x), D)
235 ------------------------------------------------------------------------
236 -- dumps a 32-bit unsigned integer (for size_t)
237 ------------------------------------------------------------------------
238 function luaU:DumpSize(x, D)
239 self:DumpBlock(self:from_int(x), D)
242 ------------------------------------------------------------------------
243 -- dumps a LUA_NUMBER (hard-coded as a double)
244 ------------------------------------------------------------------------
245 function luaU:DumpNumber(x, D)
246 self:DumpBlock(self:from_double(x), D)
249 ------------------------------------------------------------------------
250 -- dumps a Lua string
251 ------------------------------------------------------------------------
252 function luaU:DumpString(s, D)
256 s = s.."\0" -- include trailing '\0'
257 self:DumpSize(string.len(s), D)
262 ------------------------------------------------------------------------
263 -- dumps instruction block from function prototype
264 ------------------------------------------------------------------------
265 function luaU:DumpCode(f, D)
270 self:DumpBlock(luaP:Instruction(f.code[i]), D)
274 ------------------------------------------------------------------------
275 -- dumps local variable names from function prototype
276 ------------------------------------------------------------------------
277 function luaU:DumpLocals(f, D)
278 local n = f.sizelocvars
281 --printf("[DUMPLOCALS] dumping local var #%i = %s", i, tostringv(f.locvars[i]))
282 self:DumpString(f.locvars[i].varname, D)
283 self:DumpInt(f.locvars[i].startpc, D)
284 self:DumpInt(f.locvars[i].endpc, D)
288 ------------------------------------------------------------------------
289 -- dumps line information from function prototype
290 ------------------------------------------------------------------------
291 function luaU:DumpLines(f, D)
292 local n = f.sizelineinfo
296 self:DumpInt(f.lineinfo[i], D) -- was DumpBlock
300 ------------------------------------------------------------------------
301 -- dump upvalue names from function prototype
302 ------------------------------------------------------------------------
303 function luaU:DumpUpvalues(f, D)
304 local n = f.sizeupvalues
307 self:DumpString(f.upvalues[i], D)
311 ------------------------------------------------------------------------
312 -- dump constant pool from function prototype
313 -- * nvalue(o) and tsvalue(o) macros removed
314 ------------------------------------------------------------------------
315 function luaU:DumpConstants(f, D)
319 local o = f.k[i] -- TObject
320 local tt = self:ttype(o)
323 if tt == self.LUA_TNUMBER then
324 self:DumpNumber(o.value, D)
325 elseif tt == self.LUA_TSTRING then
326 self:DumpString(o.value, D)
327 elseif tt == self.LUA_TBOOLEAN then
328 self:DumpByte (o.value and 1 or 0, D)
329 elseif tt == self.LUA_TNIL then
331 assert(false) -- cannot happen
337 function luaU:DumpProtos (f, D)
342 self:DumpFunction(f.p[i], f.source, D)
346 ------------------------------------------------------------------------
347 -- dump child function prototypes from function prototype
348 --FF completely reworked for 5.1 format
349 ------------------------------------------------------------------------
350 function luaU:DumpFunction(f, p, D)
351 local source = f.source
352 if source == p then source = nil end
353 self:DumpString(source, D)
354 self:DumpInt(f.lineDefined, D)
355 self:DumpInt(42, D) -- lastlinedefined, not implemented, FIXME
356 -- self:DumpInt(f.lastlineDefined, D)
357 self:DumpByte(f.nups, D)
358 self:DumpByte(f.numparams, D)
359 self:DumpByte(f.is_vararg, D)
360 self:DumpByte(f.maxstacksize, D)
362 self:DumpConstants(f, D)
363 self:DumpProtos( f, D)
365 self:DumpLocals(f, D)
366 self:DumpUpvalues(f, D)
369 ------------------------------------------------------------------------
370 -- dump Lua header section (some sizes hard-coded)
371 --FF: updated for version 5.1
372 ------------------------------------------------------------------------
373 function luaU:DumpHeader(D)
374 self:DumpLiteral(self.LUA_SIGNATURE, D)
375 self:DumpByte(self.VERSION, D)
376 self:DumpByte(self.FORMAT_VERSION, D)
377 self:DumpByte(self:endianness(), D)
378 self:DumpByte(4, D) -- sizeof(int)
379 self:DumpByte(4, D) -- sizeof(size_t)
380 self:DumpByte(4, D) -- sizeof(Instruction)
381 self:DumpByte(8, D) -- sizeof lua_Number
382 self:DumpByte(0, D) -- integral flag
385 ------------------------------------------------------------------------
386 -- dump function as precompiled chunk
387 -- * w, data are created from make_setS, make_setF
388 --FF: suppressed extraneous [L] param
389 ------------------------------------------------------------------------
390 function luaU:dump (Main, w, data)
391 local D = {} -- DumpState
395 self:DumpFunction(Main, nil, D)
396 -- added: for a chunk writer writing to a file, this final call with
397 -- nil data is to indicate to the writer to close the file
401 ------------------------------------------------------------------------
402 -- find byte order (from lundump.c)
403 -- * hard-coded to little-endian
404 ------------------------------------------------------------------------
405 function luaU:endianness()
409 -- FIXME: ugly concat-base generation in [make_setS], bufferize properly!
410 function dump_string (proto)
411 local writer, buff = luaU:make_setS()
412 luaU:dump (proto, writer, buff)
416 -- FIXME: [make_setS] sucks, perform synchronous file writing
417 function dump_file (proto, filename)
418 local writer, buff = luaU:make_setS()
419 luaU:dump (proto, writer, buff)
420 local file = io.open (filename, "wb")
421 file:write (UNIX_SHARPBANG or "")
422 file:write (buff.data)
424 if UNIX_SHARPBANG then os.execute ("chmod a+x "..filename) end