]> git.lizzy.rs Git - metalua.git/blob - src/compiler/ldump.lua
Comiler now accept `Local{ {lhs} } nodes without a rhs part.
[metalua.git] / src / compiler / ldump.lua
1 ----------------------------------------------------------------------
2 --
3 -- WARNING! You're entering a hackish area, proceed at your own risks!
4 --
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
8 -- in it.
9 --
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
13 -- on.
14 --
15 -- End of warning.
16 --
17 ----------------------------------------------------------------------
18
19 --[[--------------------------------------------------------------------
20
21   $Id: ldump.lua,v 1.3 2006/11/07 04:38:00 fab13n Exp $
22
23   ldump.lua
24   Save bytecodes in Lua
25   This file is part of Yueliang.
26
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.
30
31 ------------------------------------------------------------------------
32
33   [FF] Slightly modified, mainly to produce Lua 5.1 bytecode.
34
35   $Log: ldump.lua,v $
36   Revision 1.3  2006/11/07 04:38:00  fab13n
37   first bootstrapping version.
38
39   Revision 1.2  2006/11/05 15:08:34  fab13n
40   updated code generation, to be compliant with 5.1
41
42
43 ----------------------------------------------------------------------]]
44
45 --[[--------------------------------------------------------------------
46 -- Notes:
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
52 -- * Deleted:
53 --   luaU:DumpVector: folded into DumpLines, DumpCode
54 -- * Added:
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 ----------------------------------------------------------------------]]
66
67 module("bytecode", package.seeall)
68
69 --requires luaP
70 luaU = {}
71
72 -- constants used by dumper
73 luaU.LUA_TNIL     = 0
74 luaU.LUA_TBOOLEAN = 1
75 luaU.LUA_TNUMBER  = 3 -- (all in lua.h)
76 luaU.LUA_TSTRING  = 4
77 luaU.LUA_TNONE   = -1
78
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.
83
84 -- a multiple of PI for testing native format
85 -- multiplying by 1E7 gives non-trivial integer values
86 --luaU.TEST_NUMBER = 3.14159265358979323846E7
87
88 --[[--------------------------------------------------------------------
89 -- Additional functions to handle chunk writing
90 -- * to use make_setS and make_setF, see test_ldump.lua elsewhere
91 ----------------------------------------------------------------------]]
92
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
103   else
104     return self.LUA_TNONE  -- the rest should not appear
105   end
106 end
107
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()
114   local buff = {}
115         buff.data = ""
116   local writer =
117     function(s, buff)  -- chunk writer
118       if not s then return end
119       buff.data = buff.data..s
120     end
121   return writer, buff
122 end
123
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)
130   local buff = {}
131         buff.h = io.open(filename, "wb")
132   if not buff.h then return nil end
133   local writer =
134     function(s, buff)  -- chunk writer
135       if not buff.h then return end
136       if not s then buff.h:close(); return end
137       buff.h:write(s)
138     end
139   return writer, buff
140 end
141
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))
151   end
152   local sign = 0
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
157   elseif x == 1/0 then
158     mantissa, exponent = 0, 2047
159   else
160     mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53)
161     exponent = exponent + 1022
162   end
163   local v, byte = "" -- convert to bytes
164   x = mantissa
165   for i = 1,6 do
166     x, byte = grab_byte(x); v = v..byte -- 47:0
167   end
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
170   return v
171 end
172
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)
178   local v = ""
179   x = math.floor(x)
180   if x >= 0 then
181     for i = 1, 4 do
182       v = v..string.char(math.mod(x, 256)); x = math.floor(x / 256)
183     end
184   else -- x < 0
185     x = -x
186     local carry = 1
187     for i = 1, 4 do
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)
191     end
192   end
193   return v
194 end
195
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 ----------------------------------------------------------------------]]
202
203 ------------------------------------------------------------------------
204 -- dump a block of literal bytes
205 ------------------------------------------------------------------------
206 function luaU:DumpLiteral(s, D) self:DumpBlock(s, D) end
207
208 --[[--------------------------------------------------------------------
209 -- struct DumpState:
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 ----------------------------------------------------------------------]]
214
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
220
221 ------------------------------------------------------------------------
222 -- dumps a single byte
223 ------------------------------------------------------------------------
224 function luaU:DumpByte(y, D)
225   self:DumpBlock(string.char(y), D)
226 end
227
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)
233 end
234
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)
240 end
241
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)
247 end
248
249 ------------------------------------------------------------------------
250 -- dumps a Lua string
251 ------------------------------------------------------------------------
252 function luaU:DumpString(s, D)
253   if s == nil then
254     self:DumpSize(0, D)
255   else
256     s = s.."\0"  -- include trailing '\0'
257     self:DumpSize(string.len(s), D)
258     self:DumpBlock(s, D)
259   end
260 end
261
262 ------------------------------------------------------------------------
263 -- dumps instruction block from function prototype
264 ------------------------------------------------------------------------
265 function luaU:DumpCode(f, D)
266   local n = f.sizecode
267   self:DumpInt(n, D)
268   --was DumpVector
269   for i = 0, n - 1 do
270     self:DumpBlock(luaP:Instruction(f.code[i]), D)
271   end
272 end
273
274 ------------------------------------------------------------------------
275 -- dumps local variable names from function prototype
276 ------------------------------------------------------------------------
277 function luaU:DumpLocals(f, D)
278   local n = f.sizelocvars
279   self:DumpInt(n, D)
280   for i = 0, n - 1 do
281     -- Dirty temporary fix: 
282     -- `Stat{ } keeps properly count of the number of local vars,
283     -- but fails to keep score of their debug info (names).
284     -- It therefore might happen that #f.localvars < f.sizelocvars, or
285     -- that a variable's startpc and endpc fields are left unset.    
286     local var = f.locvars[i]
287     if not var then break end 
288     -- printf("[DUMPLOCALS] dumping local var #%i = %s", i, table.tostring(var))
289     self:DumpString(var.varname, D)
290     self:DumpInt(var.startpc or 0, D)
291     self:DumpInt(var.endpc or 0, D)
292   end
293 end
294
295 ------------------------------------------------------------------------
296 -- dumps line information from function prototype
297 ------------------------------------------------------------------------
298 function luaU:DumpLines(f, D)
299   local n = f.sizelineinfo
300   self:DumpInt(n, D)
301   --was DumpVector
302   for i = 0, n - 1 do
303     self:DumpInt(f.lineinfo[i], D)  -- was DumpBlock
304   end
305 end
306
307 ------------------------------------------------------------------------
308 -- dump upvalue names from function prototype
309 ------------------------------------------------------------------------
310 function luaU:DumpUpvalues(f, D)
311   local n = f.sizeupvalues
312   self:DumpInt(n, D)
313   for i = 0, n - 1 do
314     self:DumpString(f.upvalues[i], D)
315   end
316 end
317
318 ------------------------------------------------------------------------
319 -- dump constant pool from function prototype
320 -- * nvalue(o) and tsvalue(o) macros removed
321 ------------------------------------------------------------------------
322 function luaU:DumpConstants(f, D)
323   local n = f.sizek
324   self:DumpInt(n, D)
325   for i = 0, n - 1 do
326     local o = f.k[i]  -- TObject
327     local tt = self:ttype(o)
328     assert (tt >= 0)
329     self:DumpByte(tt, D)
330     if tt == self.LUA_TNUMBER then
331        self:DumpNumber(o.value, D)
332     elseif tt == self.LUA_TSTRING then
333        self:DumpString(o.value, D)
334     elseif tt == self.LUA_TBOOLEAN then
335        self:DumpByte (o.value and 1 or 0, D)
336     elseif tt == self.LUA_TNIL then
337     else
338       assert(false)  -- cannot happen
339     end
340   end
341 end
342
343
344 function luaU:DumpProtos (f, D)
345   local n = f.sizep
346   assert (n)
347   self:DumpInt(n, D)
348   for i = 0, n - 1 do
349     self:DumpFunction(f.p[i], f.source, D)
350   end
351 end
352
353 ------------------------------------------------------------------------
354 -- dump child function prototypes from function prototype
355 --FF completely reworked for 5.1 format
356 ------------------------------------------------------------------------
357 function luaU:DumpFunction(f, p, D)
358    -- print "Dumping function:"
359    -- table.print(f, 60)
360
361   local source = f.source
362   if source == p then source = nil end
363   self:DumpString(source, D)
364   self:DumpInt(f.lineDefined, D)
365   self:DumpInt(42, D) -- lastlinedefined, not implemented, FIXME
366 --  self:DumpInt(f.lastlineDefined, D)
367   self:DumpByte(f.nups, D)
368   self:DumpByte(f.numparams, D)
369   self:DumpByte(f.is_vararg, D)
370   self:DumpByte(f.maxstacksize, D)
371   self:DumpCode(f, D)
372   self:DumpConstants(f, D)
373   self:DumpProtos( f, D)
374   self:DumpLines(f, D)
375   self:DumpLocals(f, D)
376   self:DumpUpvalues(f, D)
377 end
378
379 ------------------------------------------------------------------------
380 -- dump Lua header section (some sizes hard-coded)
381 --FF: updated for version 5.1
382 ------------------------------------------------------------------------
383 function luaU:DumpHeader(D)
384   self:DumpLiteral(self.LUA_SIGNATURE, D)
385   self:DumpByte(self.VERSION, D)
386   self:DumpByte(self.FORMAT_VERSION, D)
387   self:DumpByte(self:endianness(), D)
388   self:DumpByte(4, D)  -- sizeof(int)
389   self:DumpByte(4, D)  -- sizeof(size_t)
390   self:DumpByte(4, D)  -- sizeof(Instruction)
391   self:DumpByte(8, D)  -- sizeof lua_Number
392   self:DumpByte(0, D)  -- integral flag
393 end
394
395 ------------------------------------------------------------------------
396 -- dump function as precompiled chunk
397 -- * w, data are created from make_setS, make_setF
398 --FF: suppressed extraneous [L] param
399 ------------------------------------------------------------------------
400 function luaU:dump (Main, w, data)
401   local D = {}  -- DumpState
402   D.write = w
403   D.data = data
404   self:DumpHeader(D)
405   self:DumpFunction(Main, nil, D)
406   -- added: for a chunk writer writing to a file, this final call with
407   -- nil data is to indicate to the writer to close the file
408   D.write(nil, D.data)
409 end
410
411 ------------------------------------------------------------------------
412 -- find byte order (from lundump.c)
413 -- * hard-coded to little-endian
414 ------------------------------------------------------------------------
415 function luaU:endianness()
416   return 1
417 end
418
419 -- FIXME: ugly concat-base generation in [make_setS], bufferize properly! 
420 function dump_string (proto)
421    local writer, buff = luaU:make_setS()
422    luaU:dump (proto, writer, buff)
423    return buff.data
424 end
425
426 -- FIXME: [make_setS] sucks, perform synchronous file writing
427 function dump_file (proto, filename)
428    local writer, buff = luaU:make_setS()
429    luaU:dump (proto, writer, buff)
430    local file = io.open (filename, "wb")
431    file:write (UNIX_SHARPBANG or "")
432    file:write (buff.data)
433    io.close(file)
434    if UNIX_SHARPBANG then os.execute ("chmod a+x "..filename) end
435 end