· 6 years ago · Dec 27, 2019, 02:08 AM
1--[[--------------------------------------------------------------------
2
3 ldump.lua
4 Save precompiled Lua chunks
5 This file is part of Yueliang.
6
7 Copyright (c) 2006 Kein-Hong Man <khman@users.sf.net>
8 The COPYRIGHT file describes the conditions
9 under which this software may be distributed.
10
11 See the ChangeLog for more information.
12
13----------------------------------------------------------------------]]
14
15--[[--------------------------------------------------------------------
16-- Notes:
17-- * WARNING! byte order (little endian) and data type sizes for header
18-- signature values hard-coded; see luaU:header
19-- * chunk writer generators are included, see below
20-- * one significant difference is that instructions are still in table
21-- form (with OP/A/B/C/Bx fields) and luaP:Instruction() is needed to
22-- convert them into 4-char strings
23--
24-- Not implemented:
25-- * DumpVar, DumpMem has been removed
26-- * DumpVector folded into folded into DumpDebug, DumpCode
27--
28-- Added:
29-- * for convenience, the following two functions have been added:
30-- luaU:make_setS: create a chunk writer that writes to a string
31-- luaU:make_setF: create a chunk writer that writes to a file
32-- (lua.h contains a typedef for lua_Writer/lua_Chunkwriter, and
33-- a Lua-based implementation exists, writer() in lstrlib.c)
34-- * luaU:ttype(o) (from lobject.h)
35-- * for converting number types to its binary equivalent:
36-- luaU:from_double(x): encode double value for writing
37-- luaU:from_int(x): encode integer value for writing
38-- (error checking is limited for these conversion functions)
39-- (double conversion does not support denormals or NaNs)
40--
41-- Changed in 5.1.x:
42-- * the dumper was mostly rewritten in Lua 5.1.x, so notes on the
43-- differences between 5.0.x and 5.1.x is limited
44-- * LUAC_VERSION bumped to 0x51, LUAC_FORMAT added
45-- * developer is expected to adjust LUAC_FORMAT in order to identify
46-- non-standard binary chunk formats
47-- * header signature code is smaller, has been simplified, and is
48-- tested as a single unit; its logic is shared with the undumper
49-- * no more endian conversion, invalid endianness mean rejection
50-- * opcode field sizes are no longer exposed in the header
51-- * code moved to front of a prototype, followed by constants
52-- * debug information moved to the end of the binary chunk, and the
53-- relevant functions folded into a single function
54-- * luaU:dump returns a writer status code
55-- * chunk writer now implements status code because dumper uses it
56-- * luaU:endianness removed
57----------------------------------------------------------------------]]
58
59--requires luaP
60local luaU = {}
61local luaP = require(script.Parent.LuaP)
62
63-- mark for precompiled code ('<esc>Lua') (from lua.h)
64luaU.LUA_SIGNATURE = "\27Lua"
65
66-- constants used by dumper (from lua.h)
67luaU.LUA_TNUMBER = 3
68luaU.LUA_TSTRING = 4
69luaU.LUA_TNIL = 0
70luaU.LUA_TBOOLEAN = 1
71luaU.LUA_TNONE = -1
72
73-- constants for header of binary files (from lundump.h)
74luaU.LUAC_VERSION = 0x51 -- this is Lua 5.1
75luaU.LUAC_FORMAT = 0 -- this is the official format
76luaU.LUAC_HEADERSIZE = 12 -- size of header of binary files
77
78--[[--------------------------------------------------------------------
79-- Additional functions to handle chunk writing
80-- * to use make_setS and make_setF, see test_ldump.lua elsewhere
81----------------------------------------------------------------------]]
82
83------------------------------------------------------------------------
84-- create a chunk writer that writes to a string
85-- * returns the writer function and a table containing the string
86-- * to get the final result, look in buff.data
87------------------------------------------------------------------------
88function luaU:make_setS()
89 local buff = {}
90 buff.data = ""
91 local writer =
92 function(s, buff) -- chunk writer
93 if not s then return 0 end
94 buff.data = buff.data..s
95 return 0
96 end
97 return writer, buff
98end
99
100------------------------------------------------------------------------
101-- create a chunk writer that writes to a file
102-- * returns the writer function and a table containing the file handle
103-- * if a nil is passed, then writer should close the open file
104------------------------------------------------------------------------
105
106--[[
107function luaU:make_setF(filename)
108 local buff = {}
109 buff.h = io.open(filename, "wb")
110 if not buff.h then return nil end
111 local writer =
112 function(s, buff) -- chunk writer
113 if not buff.h then return 0 end
114 if not s then
115 if buff.h:close() then return 0 end
116 else
117 if buff.h:write(s) then return 0 end
118 end
119 return 1
120 end
121 return writer, buff
122end--]]
123
124------------------------------------------------------------------------
125-- works like the lobject.h version except that TObject used in these
126-- scripts only has a 'value' field, no 'tt' field (native types used)
127------------------------------------------------------------------------
128function luaU:ttype(o)
129 local tt = type(o.value)
130 if tt == "number" then return self.LUA_TNUMBER
131 elseif tt == "string" then return self.LUA_TSTRING
132 elseif tt == "nil" then return self.LUA_TNIL
133 elseif tt == "boolean" then return self.LUA_TBOOLEAN
134 else
135 return self.LUA_TNONE -- the rest should not appear
136 end
137end
138
139-----------------------------------------------------------------------
140-- converts a IEEE754 double number to an 8-byte little-endian string
141-- * luaU:from_double() and luaU:from_int() are adapted from ChunkBake
142-- * supports +/- Infinity, but not denormals or NaNs
143-----------------------------------------------------------------------
144function luaU:from_double(x)
145 local function grab_byte(v)
146 local c = v % 256
147 return (v - c) / 256, string.char(c)
148 end
149 local sign = 0
150 if x < 0 then sign = 1; x = -x end
151 local mantissa, exponent = math.frexp(x)
152 if x == 0 then -- zero
153 mantissa, exponent = 0, 0
154 elseif x == 1/0 then
155 mantissa, exponent = 0, 2047
156 else
157 mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53)
158 exponent = exponent + 1022
159 end
160 local v, byte = "" -- convert to bytes
161 x = math.floor(mantissa)
162 for i = 1,6 do
163 x, byte = grab_byte(x); v = v..byte -- 47:0
164 end
165 x, byte = grab_byte(exponent * 16 + x); v = v..byte -- 55:48
166 x, byte = grab_byte(sign * 128 + x); v = v..byte -- 63:56
167 return v
168end
169
170-----------------------------------------------------------------------
171-- converts a number to a little-endian 32-bit integer string
172-- * input value assumed to not overflow, can be signed/unsigned
173-----------------------------------------------------------------------
174function luaU:from_int(x)
175 local v = ""
176 x = math.floor(x)
177 if x < 0 then x = 4294967296 + x end -- ULONG_MAX+1
178 for i = 1, 4 do
179 local c = x % 256
180 v = v..string.char(c); x = math.floor(x / 256)
181 end
182 return v
183end
184
185--[[--------------------------------------------------------------------
186-- Functions to make a binary chunk
187-- * many functions have the size parameter removed, since output is
188-- in the form of a string and some sizes are implicit or hard-coded
189----------------------------------------------------------------------]]
190
191--[[--------------------------------------------------------------------
192-- struct DumpState:
193-- L -- lua_State (not used in this script)
194-- writer -- lua_Writer (chunk writer function)
195-- data -- void* (chunk writer context or data already written)
196-- strip -- if true, don't write any debug information
197-- status -- if non-zero, an error has occured
198----------------------------------------------------------------------]]
199
200------------------------------------------------------------------------
201-- dumps a block of bytes
202-- * lua_unlock(D.L), lua_lock(D.L) unused
203------------------------------------------------------------------------
204function luaU:DumpBlock(b, D)
205 if D.status == 0 then
206 -- lua_unlock(D->L);
207 D.status = D.write(b, D.data)
208 -- lua_lock(D->L);
209 end
210end
211
212------------------------------------------------------------------------
213-- dumps a char
214------------------------------------------------------------------------
215function luaU:DumpChar(y, D)
216 self:DumpBlock(string.char(y), D)
217end
218
219------------------------------------------------------------------------
220-- dumps a 32-bit signed or unsigned integer (for int) (hard-coded)
221------------------------------------------------------------------------
222function luaU:DumpInt(x, D)
223 self:DumpBlock(self:from_int(x), D)
224end
225
226------------------------------------------------------------------------
227-- dumps a lua_Number (hard-coded as a double)
228------------------------------------------------------------------------
229function luaU:DumpNumber(x, D)
230 self:DumpBlock(self:from_double(x), D)
231end
232
233------------------------------------------------------------------------
234-- dumps a Lua string (size type is hard-coded)
235------------------------------------------------------------------------
236function luaU:DumpString(s, D)
237 if s == nil then
238 self:DumpInt(0, D)
239 else
240 s = s.."\0" -- include trailing '\0'
241 self:DumpInt(#s, D)
242 self:DumpBlock(s, D)
243 end
244end
245
246------------------------------------------------------------------------
247-- dumps instruction block from function prototype
248------------------------------------------------------------------------
249function luaU:DumpCode(f, D)
250 local n = f.sizecode
251 --was DumpVector
252 self:DumpInt(n, D)
253 for i = 0, n - 1 do
254 self:DumpBlock(luaP:Instruction(f.code[i]), D)
255 end
256end
257
258------------------------------------------------------------------------
259-- dump constant pool from function prototype
260-- * bvalue(o), nvalue(o) and rawtsvalue(o) macros removed
261------------------------------------------------------------------------
262function luaU:DumpConstants(f, D)
263 local n = f.sizek
264 self:DumpInt(n, D)
265 for i = 0, n - 1 do
266 local o = f.k[i] -- TValue
267 local tt = self:ttype(o)
268 self:DumpChar(tt, D)
269 if tt == self.LUA_TNIL then
270 elseif tt == self.LUA_TBOOLEAN then
271 self:DumpChar(o.value and 1 or 0, D)
272 elseif tt == self.LUA_TNUMBER then
273 self:DumpNumber(o.value, D)
274 elseif tt == self.LUA_TSTRING then
275 self:DumpString(o.value, D)
276 else
277 --lua_assert(0) -- cannot happen
278 end
279 end
280 n = f.sizep
281 self:DumpInt(n, D)
282 for i = 0, n - 1 do
283 self:DumpFunction(f.p[i], f.source, D)
284 end
285end
286
287------------------------------------------------------------------------
288-- dump debug information
289------------------------------------------------------------------------
290function luaU:DumpDebug(f, D)
291 local n
292 n = D.strip and 0 or f.sizelineinfo -- dump line information
293 --was DumpVector
294 self:DumpInt(n, D)
295 for i = 0, n - 1 do
296 self:DumpInt(f.lineinfo[i], D)
297 end
298 n = D.strip and 0 or f.sizelocvars -- dump local information
299 self:DumpInt(n, D)
300 for i = 0, n - 1 do
301 self:DumpString(f.locvars[i].varname, D)
302 self:DumpInt(f.locvars[i].startpc, D)
303 self:DumpInt(f.locvars[i].endpc, D)
304 end
305 n = D.strip and 0 or f.sizeupvalues -- dump upvalue information
306 self:DumpInt(n, D)
307 for i = 0, n - 1 do
308 self:DumpString(f.upvalues[i], D)
309 end
310end
311
312------------------------------------------------------------------------
313-- dump child function prototypes from function prototype
314------------------------------------------------------------------------
315function luaU:DumpFunction(f, p, D)
316 local source = f.source
317 if source == p or D.strip then source = nil end
318 self:DumpString(source, D)
319 self:DumpInt(f.lineDefined, D)
320 self:DumpInt(f.lastlinedefined, D)
321 self:DumpChar(f.nups, D)
322 self:DumpChar(f.numparams, D)
323 self:DumpChar(f.is_vararg, D)
324 self:DumpChar(f.maxstacksize, D)
325 self:DumpCode(f, D)
326 self:DumpConstants(f, D)
327 self:DumpDebug(f, D)
328end
329
330------------------------------------------------------------------------
331-- dump Lua header section (some sizes hard-coded)
332------------------------------------------------------------------------
333function luaU:DumpHeader(D)
334 local h = self:header()
335 assert(#h == self.LUAC_HEADERSIZE) -- fixed buffer now an assert
336 self:DumpBlock(h, D)
337end
338
339------------------------------------------------------------------------
340-- make header (from lundump.c)
341-- returns the header string
342------------------------------------------------------------------------
343function luaU:header()
344 local x = 1
345 return self.LUA_SIGNATURE..
346 string.char(
347 self.LUAC_VERSION,
348 self.LUAC_FORMAT,
349 x, -- endianness (1=little)
350 4, -- sizeof(int)
351 4, -- sizeof(size_t)
352 4, -- sizeof(Instruction)
353 8, -- sizeof(lua_Number)
354 0) -- is lua_Number integral?
355end
356
357------------------------------------------------------------------------
358-- dump Lua function as precompiled chunk
359-- (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip)
360-- * w, data are created from make_setS, make_setF
361------------------------------------------------------------------------
362function luaU:dump(L, f, w, data, strip)
363 local D = {} -- DumpState
364 D.L = L
365 D.write = w
366 D.data = data
367 D.strip = strip
368 D.status = 0
369 self:DumpHeader(D)
370 self:DumpFunction(f, nil, D)
371 -- added: for a chunk writer writing to a file, this final call with
372 -- nil data is to indicate to the writer to close the file
373 D.write(nil, D.data)
374 return D.status
375end
376
377return luaU