]> git.lizzy.rs Git - metalua.git/blob - metalua/compiler/bytecode/ldump.lua
Merge branch 'master' of ssh://git.eclipse.org/gitroot/koneki/org.eclipse.koneki...
[metalua.git] / metalua / compiler / bytecode / ldump.lua
1 -------------------------------------------------------------------------------
2 -- Copyright (c) 2005-2013 Kein-Hong Man, Fabien Fleutot and others.
3 --
4 -- All rights reserved.
5 --
6 -- This program and the accompanying materials are made available
7 -- under the terms of the Eclipse Public License v1.0 which
8 -- accompanies this distribution, and is available at
9 -- http://www.eclipse.org/legal/epl-v10.html
10 --
11 -- This program and the accompanying materials are also made available
12 -- under the terms of the MIT public license which accompanies this
13 -- distribution, and is available at http://www.lua.org/license.html
14 --
15 -- Contributors:
16 --     Kein-Hong Man  - Initial implementation for Lua 5.0, part of Yueliang
17 --     Fabien Fleutot - Port to Lua 5.1, integration with Metalua
18 --
19 -------------------------------------------------------------------------------
20
21 --[[--------------------------------------------------------------------
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 ----------------------------------------------------------------------]]
36
37 --[[--------------------------------------------------------------------
38 -- Notes:
39 -- * LUA_NUMBER (double), byte order (little endian) and some other
40 --   header values hard-coded; see other notes below...
41 -- * One significant difference is that instructions are still in table
42 --   form (with OP/A/B/C/Bx fields) and luaP:Instruction() is needed to
43 --   convert them into 4-char strings
44 -- * Deleted:
45 --   luaU:DumpVector: folded into DumpLines, DumpCode
46 -- * Added:
47 --   luaU:endianness() (from lundump.c)
48 --   luaU:make_setS: create a chunk writer that writes to a string
49 --   luaU:make_setF: create a chunk writer that writes to a file
50 --     (lua.h contains a typedef for a Chunkwriter pointer, and
51 --      a Lua-based implementation exists, writer() in lstrlib.c)
52 --   luaU:from_double(x): encode double value for writing
53 --   luaU:from_int(x): encode integer value for writing
54 --     (error checking is limited for these conversion functions)
55 --     (double conversion does not support denormals or NaNs)
56 --   luaU:ttype(o) (from lobject.h)
57 ----------------------------------------------------------------------]]
58
59 local luaP = require 'metalua.compiler.bytecode.lopcodes'
60
61 local M = { }
62
63 local format = { }
64 format.header = string.dump(function()end):sub(1, 12)
65 format.little_endian, format.int_size, 
66 format.size_t_size,   format.instr_size, 
67 format.number_size,   format.integral = format.header:byte(7, 12)
68 format.little_endian = format.little_endian~=0
69 format.integral      = format.integral     ~=0
70
71 assert(format.integral or format.number_size==8, "Number format not supported by dumper")
72 assert(format.little_endian, "Big endian architectures not supported by dumper")
73
74 --requires luaP
75 local luaU = { }
76 M.luaU = luaU
77
78 luaU.format = format
79
80 -- constants used by dumper
81 luaU.LUA_TNIL     = 0
82 luaU.LUA_TBOOLEAN = 1
83 luaU.LUA_TNUMBER  = 3 -- (all in lua.h)
84 luaU.LUA_TSTRING  = 4
85 luaU.LUA_TNONE   = -1
86
87 -- definitions for headers of binary files
88 --luaU.LUA_SIGNATURE = "\27Lua"   -- binary files start with "<esc>Lua"
89 --luaU.VERSION = 81               -- 0x50; last format change was in 5.0
90 --luaU.FORMAT_VERSION = 0         -- 0 is official version. yeah I know I'm a liar.
91
92 -- a multiple of PI for testing native format
93 -- multiplying by 1E7 gives non-trivial integer values
94 --luaU.TEST_NUMBER = 3.14159265358979323846E7
95
96 --[[--------------------------------------------------------------------
97 -- Additional functions to handle chunk writing
98 -- * to use make_setS and make_setF, see test_ldump.lua elsewhere
99 ----------------------------------------------------------------------]]
100
101 ------------------------------------------------------------------------
102 -- works like the lobject.h version except that TObject used in these
103 -- scripts only has a 'value' field, no 'tt' field (native types used)
104 ------------------------------------------------------------------------
105 function luaU:ttype(o)
106   local tt = type(o.value)
107   if     tt == "number"  then return self.LUA_TNUMBER
108   elseif tt == "string"  then return self.LUA_TSTRING
109   elseif tt == "nil"     then return self.LUA_TNIL
110   elseif tt == "boolean" then return self.LUA_TBOOLEAN
111   else
112     return self.LUA_TNONE  -- the rest should not appear
113   end
114 end
115
116 ------------------------------------------------------------------------
117 -- create a chunk writer that writes to a string
118 -- * returns the writer function and a table containing the string
119 -- * to get the final result, look in buff.data
120 ------------------------------------------------------------------------
121 function luaU:make_setS()
122   local buff = {}
123         buff.data = ""
124   local writer =
125     function(s, buff)  -- chunk writer
126       if not s then return end
127       buff.data = buff.data..s
128     end
129   return writer, buff
130 end
131
132 ------------------------------------------------------------------------
133 -- create a chunk writer that writes to a file
134 -- * returns the writer function and a table containing the file handle
135 -- * if a nil is passed, then writer should close the open file
136 ------------------------------------------------------------------------
137 function luaU:make_setF(filename)
138   local buff = {}
139         buff.h = io.open(filename, "wb")
140   if not buff.h then return nil end
141   local writer =
142     function(s, buff)  -- chunk writer
143       if not buff.h then return end
144       if not s then buff.h:close(); return end
145       buff.h:write(s)
146     end
147   return writer, buff
148 end
149
150 -----------------------------------------------------------------------
151 -- converts a IEEE754 double number to an 8-byte little-endian string
152 -- * luaU:from_double() and luaU:from_int() are from ChunkBake project
153 -- * supports +/- Infinity, but not denormals or NaNs
154 -----------------------------------------------------------------------
155 function luaU:from_double(x)
156   local function grab_byte(v)
157     return math.floor(v / 256),
158            string.char(math.mod(math.floor(v), 256))
159   end
160   local sign = 0
161   if x < 0 then sign = 1; x = -x end
162   local mantissa, exponent = math.frexp(x)
163   if x == 0 then -- zero
164     mantissa, exponent = 0, 0
165   elseif x == 1/0 then
166     mantissa, exponent = 0, 2047
167   else
168     mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53)
169     exponent = exponent + 1022
170   end
171   local v, byte = "" -- convert to bytes
172   x = mantissa
173   for i = 1,6 do
174     x, byte = grab_byte(x); v = v..byte -- 47:0
175   end
176   x, byte = grab_byte(exponent * 16 + x); v = v..byte -- 55:48
177   x, byte = grab_byte(sign * 128 + x); v = v..byte -- 63:56
178   return v
179 end
180
181 -----------------------------------------------------------------------
182 -- converts a number to a little-endian 32-bit integer string
183 -- * input value assumed to not overflow, can be signed/unsigned
184 -----------------------------------------------------------------------
185 function luaU:from_int(x, size)
186   local v = ""
187   x = math.floor(x)
188   if x >= 0 then
189     for i = 1, size do
190       v = v..string.char(math.mod(x, 256)); x = math.floor(x / 256)
191     end
192   else -- x < 0
193     x = -x
194     local carry = 1
195     for i = 1, size do
196       local c = 255 - math.mod(x, 256) + carry
197       if c == 256 then c = 0; carry = 1 else carry = 0 end
198       v = v..string.char(c); x = math.floor(x / 256)
199     end
200   end
201   return v
202 end
203
204 --[[--------------------------------------------------------------------
205 -- Functions to make a binary chunk
206 -- * many functions have the size parameter removed, since output is
207 --   in the form of a string and some sizes are implicit or hard-coded
208 -- * luaU:DumpVector has been deleted (used in DumpCode & DumpLines)
209 ----------------------------------------------------------------------]]
210
211 ------------------------------------------------------------------------
212 -- dump a block of literal bytes
213 ------------------------------------------------------------------------
214 function luaU:DumpLiteral(s, D) self:DumpBlock(s, D) end
215
216 --[[--------------------------------------------------------------------
217 -- struct DumpState:
218 --   L  -- lua_State (not used in this script)
219 --   write  -- lua_Chunkwriter (chunk writer function)
220 --   data  -- void* (chunk writer context or data already written)
221 ----------------------------------------------------------------------]]
222
223 ------------------------------------------------------------------------
224 -- dumps a block of bytes
225 -- * lua_unlock(D.L), lua_lock(D.L) deleted
226 ------------------------------------------------------------------------
227 function luaU:DumpBlock(b, D) D.write(b, D.data) end
228
229 ------------------------------------------------------------------------
230 -- dumps a single byte
231 ------------------------------------------------------------------------
232 function luaU:DumpByte(y, D)
233   self:DumpBlock(string.char(y), D)
234 end
235
236 ------------------------------------------------------------------------
237 -- dumps a signed integer of size `format.int_size` (for int)
238 ------------------------------------------------------------------------
239 function luaU:DumpInt(x, D)
240   self:DumpBlock(self:from_int(x, format.int_size), D)
241 end
242
243 ------------------------------------------------------------------------
244 -- dumps an unsigned integer of size `format.size_t_size` (for size_t)
245 ------------------------------------------------------------------------
246 function luaU:DumpSize(x, D)
247   self:DumpBlock(self:from_int(x, format.size_t_size), D)
248 end
249
250 ------------------------------------------------------------------------
251 -- dumps a LUA_NUMBER; can be an int or double depending on the VM.
252 ------------------------------------------------------------------------
253 function luaU:DumpNumber(x, D)
254    if format.integral then
255       self:DumpBlock(self:from_int(x, format.number_size), D)
256    else
257       self:DumpBlock(self:from_double(x), D)
258    end
259 end
260
261 ------------------------------------------------------------------------
262 -- dumps a Lua string
263 ------------------------------------------------------------------------
264 function luaU:DumpString(s, D)
265   if s == nil then
266     self:DumpSize(0, D)
267   else
268     s = s.."\0"  -- include trailing '\0'
269     self:DumpSize(string.len(s), D)
270     self:DumpBlock(s, D)
271   end
272 end
273
274 ------------------------------------------------------------------------
275 -- dumps instruction block from function prototype
276 ------------------------------------------------------------------------
277 function luaU:DumpCode(f, D)
278   local n = f.sizecode
279   self:DumpInt(n, D)
280   --was DumpVector
281   for i = 0, n - 1 do
282     self:DumpBlock(luaP:Instruction(f.code[i]), D)
283   end
284 end
285
286 ------------------------------------------------------------------------
287 -- dumps local variable names from function prototype
288 ------------------------------------------------------------------------
289 function luaU:DumpLocals(f, D)
290   local n = f.sizelocvars
291   self:DumpInt(n, D)
292   for i = 0, n - 1 do
293     -- Dirty temporary fix: 
294     -- `Stat{ } keeps properly count of the number of local vars,
295     -- but fails to keep score of their debug info (names).
296     -- It therefore might happen that #f.localvars < f.sizelocvars, or
297     -- that a variable's startpc and endpc fields are left unset.
298     -- FIXME: This might not be needed anymore, check the bug report
299     --        by J. Belmonte.
300     local var = f.locvars[i]
301     if not var then break end 
302     -- printf("[DUMPLOCALS] dumping local var #%i = %s", i, table.tostring(var))
303     self:DumpString(var.varname, D)
304     self:DumpInt(var.startpc or 0, D)
305     self:DumpInt(var.endpc or 0, D)
306   end
307 end
308
309 ------------------------------------------------------------------------
310 -- dumps line information from function prototype
311 ------------------------------------------------------------------------
312 function luaU:DumpLines(f, D)
313   local n = f.sizelineinfo
314   self:DumpInt(n, D)
315   --was DumpVector
316   for i = 0, n - 1 do
317     self:DumpInt(f.lineinfo[i], D)  -- was DumpBlock
318     --print(i, f.lineinfo[i])
319   end
320 end
321
322 ------------------------------------------------------------------------
323 -- dump upvalue names from function prototype
324 ------------------------------------------------------------------------
325 function luaU:DumpUpvalues(f, D)
326   local n = f.sizeupvalues
327   self:DumpInt(n, D)
328   for i = 0, n - 1 do
329     self:DumpString(f.upvalues[i], D)
330   end
331 end
332
333 ------------------------------------------------------------------------
334 -- dump constant pool from function prototype
335 -- * nvalue(o) and tsvalue(o) macros removed
336 ------------------------------------------------------------------------
337 function luaU:DumpConstants(f, D)
338   local n = f.sizek
339   self:DumpInt(n, D)
340   for i = 0, n - 1 do
341     local o = f.k[i]  -- TObject
342     local tt = self:ttype(o)
343     assert (tt >= 0)
344     self:DumpByte(tt, D)
345     if tt == self.LUA_TNUMBER then
346        self:DumpNumber(o.value, D)
347     elseif tt == self.LUA_TSTRING then
348        self:DumpString(o.value, D)
349     elseif tt == self.LUA_TBOOLEAN then
350        self:DumpByte (o.value and 1 or 0, D)
351     elseif tt == self.LUA_TNIL then
352     else
353       assert(false)  -- cannot happen
354     end
355   end
356 end
357
358
359 function luaU:DumpProtos (f, D)
360   local n = f.sizep
361   assert (n)
362   self:DumpInt(n, D)
363   for i = 0, n - 1 do
364     self:DumpFunction(f.p[i], f.source, D)
365   end
366 end
367
368 function luaU:DumpDebug(f, D)
369   self:DumpLines(f, D)
370   self:DumpLocals(f, D)
371   self:DumpUpvalues(f, D)
372 end
373
374
375 ------------------------------------------------------------------------
376 -- dump child function prototypes from function prototype
377 --FF completely reworked for 5.1 format
378 ------------------------------------------------------------------------
379 function luaU:DumpFunction(f, p, D)
380    -- print "Dumping function:"
381    -- table.print(f, 60)
382
383   local source = f.source
384   if source == p then source = nil end
385   self:DumpString(source, D)
386   self:DumpInt(f.lineDefined, D)
387   self:DumpInt(f.lastLineDefined or 42, D)
388   self:DumpByte(f.nups, D)
389   self:DumpByte(f.numparams, D)
390   self:DumpByte(f.is_vararg, D)
391   self:DumpByte(f.maxstacksize, D)
392   self:DumpCode(f, D)
393   self:DumpConstants(f, D)
394   self:DumpProtos( f, D)
395   self:DumpDebug(f, D)
396 end
397
398 ------------------------------------------------------------------------
399 -- dump Lua header section (some sizes hard-coded)
400 --FF: updated for version 5.1
401 ------------------------------------------------------------------------
402 function luaU:DumpHeader(D)
403   self:DumpLiteral(format.header, D)
404 end
405
406 ------------------------------------------------------------------------
407 -- dump function as precompiled chunk
408 -- * w, data are created from make_setS, make_setF
409 --FF: suppressed extraneous [L] param
410 ------------------------------------------------------------------------
411 function luaU:dump (Main, w, data)
412   local D = {}  -- DumpState
413   D.write = w
414   D.data = data
415   self:DumpHeader(D)
416   self:DumpFunction(Main, nil, D)
417   -- added: for a chunk writer writing to a file, this final call with
418   -- nil data is to indicate to the writer to close the file
419   D.write(nil, D.data)
420 end
421
422 ------------------------------------------------------------------------
423 -- find byte order (from lundump.c)
424 -- * hard-coded to little-endian
425 ------------------------------------------------------------------------
426 function luaU:endianness()
427   return 1
428 end
429
430 -- FIXME: ugly concat-base generation in [make_setS], bufferize properly! 
431 function M.dump_string (proto)
432    local writer, buff = luaU:make_setS()
433    luaU:dump (proto, writer, buff)
434    return buff.data
435 end
436
437 -- FIXME: [make_setS] sucks, perform synchronous file writing
438 -- Now unused
439 function M.dump_file (proto, filename)
440    local writer, buff = luaU:make_setS()
441    luaU:dump (proto, writer, buff)
442    local file = io.open (filename, "wb")
443    file:write (buff.data)
444    io.close(file)
445    --if UNIX_SHARPBANG then os.execute ("chmod a+x "..filename) end
446 end
447
448 return M