7 -- chunk = string.dump(loadfile"blabla.lua") |
7 -- chunk = string.dump(loadfile"blabla.lua") |
8 -- disassembled_chunk = minichunkspy.disassemble(chunk) |
8 -- disassembled_chunk = minichunkspy.disassemble(chunk) |
9 -- chunk = minichunkspy.assemble(disassembled_chunk) |
9 -- chunk = minichunkspy.assemble(disassembled_chunk) |
10 -- assert(minichunkspy.validate(<function or chunk>)) |
10 -- assert(minichunkspy.validate(<function or chunk>)) |
11 -- |
11 -- |
12 -- Tested on little-endian 32 bit platforms. Modify |
12 -- Tested on little-endian 32 and 64 bit platforms. |
13 -- the Size_t type to be a 64 bit integer to make it work |
|
14 -- for 64 bit systems, and set BIG_ENDIAN = true for |
|
15 -- big-endian systems. |
|
16 local string, table, math = string, table, math |
13 local string, table, math = string, table, math |
17 local ipairs, setmetatable, type, assert = ipairs, setmetatable, type, assert |
14 local ipairs, setmetatable, type, assert = ipairs, setmetatable, type, assert |
18 local _ = __END_OF_GLOBALS__ |
15 local _ = __END_OF_GLOBALS__ |
19 local string_char, string_byte, string_sub = string.char, string.byte, string.sub |
16 local string_char, string_byte, string_sub = string.char, string.byte, string.sub |
|
17 local math_frexp, math_ldexp, math_abs = math.frexp, math.ldexp, math.abs |
20 local table_concat = table.concat |
18 local table_concat = table.concat |
21 local math_abs, math_ldexp, math_frexp = math.abs, math.ldexp, math.frexp |
|
22 local Inf = math.huge |
19 local Inf = math.huge |
23 local Nan = Inf - Inf |
20 local NaN = Inf - Inf |
24 |
21 |
25 local BIG_ENDIAN = false --twiddle this for your platform. |
22 local BIG_ENDIAN = false |
26 |
23 local SIZEOF_SIZE_T = 4 |
27 local function construct (class, ...) |
24 local SIZEOF_INT = 4 |
28 return class.new(class, ...) |
25 local SIZEOF_NUMBER = 8 |
|
26 |
|
27 local save_stack = {} |
|
28 |
|
29 local function save() |
|
30 save_stack[#save_stack+1] |
|
31 = {BIG_ENDIAN, SIZEOF_SIZE_T, SIZEOF_INT, SIZEOF_NUMBER} |
|
32 end |
|
33 local function restore () |
|
34 BIG_ENDIAN, SIZEOF_SIZE_T, SIZEOF_INT, SIZEOF_NUMBER |
|
35 = unpack(save_stack[#save_stack]) |
|
36 save_stack[#save_stack] = nil |
|
37 end |
|
38 |
|
39 local function construct (class, self) |
|
40 return class.new(class, self) |
29 end |
41 end |
30 |
42 |
31 local mt_memo = {} |
43 local mt_memo = {} |
32 |
44 |
33 local Field = construct{ |
45 local Field = construct{ |
87 if BIG_ENDIAN then a,b,c,d = d,c,b,a end |
99 if BIG_ENDIAN then a,b,c,d = d,c,b,a end |
88 return string_char(a,b,c,d) |
100 return string_char(a,b,c,d) |
89 end |
101 end |
90 } |
102 } |
91 |
103 |
92 local int32 = uint32{ |
104 local uint64 = Field{ |
|
105 unpack = |
|
106 function (self, bytes, ix) |
|
107 local a = uint32:unpack(bytes, ix) |
|
108 local b = uint32:unpack(bytes, ix+4) |
|
109 if BIG_ENDIAN then a,b = b,a end |
|
110 return a + b*2^32, ix+8 |
|
111 end, |
|
112 pack = |
|
113 function (self, val) |
|
114 assert(type(val) == "number", |
|
115 "unexpected value type to pack as an uint64") |
|
116 local a = val % 2^32 |
|
117 local b = (val - a) / 2^32 |
|
118 if BIG_ENDIAN then a,b = b,a end |
|
119 return uint32:pack(a) .. uint32:pack(b) |
|
120 end |
|
121 } |
|
122 |
|
123 local function explode_double(bytes, ix) |
|
124 local a = uint32:unpack(bytes, ix) |
|
125 local b = uint32:unpack(bytes, ix+4) |
|
126 if BIG_ENDIAN then a,b = b,a end --XXX: ARM mixed-endian |
|
127 |
|
128 local sig_hi = b % 2^20 |
|
129 local sig_lo = a |
|
130 local significand = sig_lo + sig_hi*2^32 |
|
131 |
|
132 b = (b - sig_hi) / 2^20 |
|
133 |
|
134 local biased_exp = b % 2^11 |
|
135 local sign = b <= biased_exp and 1 or -1 |
|
136 |
|
137 --print(sign, significand, biased_exp, "explode") |
|
138 return sign, biased_exp, significand |
|
139 end |
|
140 |
|
141 local function implode_double(sign, biased_exp, significand) |
|
142 --print(sign, significand, biased_exp, "implode") |
|
143 local sig_lo = significand % 2^32 |
|
144 local sig_hi = (significand - sig_lo) / 2^32 |
|
145 |
|
146 local a = sig_lo |
|
147 local b = ((sign < 0 and 2^11 or 0) + biased_exp)*2^20 + sig_hi |
|
148 |
|
149 if BIG_ENDIAN then a,b = b,a end --XXX: ARM mixed-endian |
|
150 return uint32.pack(nil, a) .. uint32.pack(nil, b) |
|
151 end |
|
152 |
|
153 local function math_sign(x) |
|
154 if x ~= x then return x end --sign of NaN is NaN |
|
155 if x == 0 then x = 1/x end --extract sign of zero |
|
156 return x > 0 and 1 or -1 |
|
157 end |
|
158 |
|
159 local SMALLEST_SUBNORMAL = math_ldexp(1, -1022 - 52) |
|
160 local SMALLEST_NORMAL = SMALLEST_SUBNORMAL * 2^52 |
|
161 local LARGEST_SUBNORMAL = math_ldexp(2^52 - 1, -1022 - 52) |
|
162 local LARGEST_NORMAL = math_ldexp(2^53 - 1, 1023 - 52) |
|
163 assert(SMALLEST_SUBNORMAL ~= 0.0 and SMALLEST_SUBNORMAL / 2 == 0.0) |
|
164 assert(LARGEST_NORMAL ~= Inf) |
|
165 assert(LARGEST_NORMAL * 2 == Inf) |
|
166 |
|
167 local double = Field{ |
|
168 unpack = |
|
169 function (self, bytes, ix) |
|
170 local sign, biased_exp, significand = explode_double(bytes, ix) |
|
171 |
|
172 local val |
|
173 if biased_exp == 0 then --subnormal |
|
174 val = math_ldexp(significand, -1022 - 52) |
|
175 elseif biased_exp == 2047 then |
|
176 val = significand == 0 and Inf or NaN --XXX: loses NaN mantissa |
|
177 else --normal |
|
178 val = math_ldexp(2^52 + significand, biased_exp - 1023 - 52) |
|
179 end |
|
180 val = sign*val |
|
181 return val, ix+8 |
|
182 end, |
|
183 |
|
184 pack = |
|
185 function (self, val) |
|
186 if val ~= val then |
|
187 return implode_double(1,2047,2^52-1) --XXX: loses NaN mantissa |
|
188 end |
|
189 |
|
190 local sign = math_sign(val) |
|
191 val = math_abs(val) |
|
192 |
|
193 if val == Inf then return implode_double(sign, 2047, 0) end |
|
194 if val == 0 then return implode_double(sign, 0, 0) end |
|
195 |
|
196 local biased_exp, significand |
|
197 |
|
198 if val <= LARGEST_SUBNORMAL then |
|
199 biased_exp = 0 |
|
200 significand = val / SMALLEST_SUBNORMAL |
|
201 else |
|
202 local frac, exp = math_frexp(val) |
|
203 significand = (2*frac - 1)*2^52 |
|
204 biased_exp = exp + 1022 |
|
205 end |
|
206 return implode_double(sign, biased_exp, significand) |
|
207 end |
|
208 } |
|
209 |
|
210 local Byte = uint8 |
|
211 |
|
212 local IntegralTypes = { |
|
213 [4] = uint32, |
|
214 [8] = uint64 |
|
215 } |
|
216 |
|
217 local FloatTypes = { |
|
218 [4] = float, |
|
219 [8] = double |
|
220 } |
|
221 |
|
222 local Size_t = Field{ |
93 unpack = function (self, bytes, ix) |
223 unpack = function (self, bytes, ix) |
94 local val, ix = uint32:unpack(bytes, ix) |
224 return IntegralTypes[SIZEOF_SIZE_T]:unpack(bytes, ix) |
95 return val < 2^32 and val or (val - 2^31), ix |
225 end, |
96 end |
226 pack = function (self, val) |
97 } |
227 return IntegralTypes[SIZEOF_SIZE_T]:pack(val) |
98 |
228 end, |
99 local Byte = uint8 |
229 } |
100 local Size_t = uint32 |
230 |
101 local Integer = int32 |
231 local Integer = Field{ |
|
232 unpack = function (self, bytes, ix) |
|
233 return IntegralTypes[SIZEOF_INT]:unpack(bytes, ix) |
|
234 end, |
|
235 pack = function (self, val) |
|
236 return IntegralTypes[SIZEOF_INT]:pack(val) |
|
237 end, |
|
238 } |
|
239 |
|
240 local Number = Field{ |
|
241 unpack = function (self, bytes, ix) |
|
242 return FloatTypes[SIZEOF_NUMBER]:unpack(bytes, ix) |
|
243 end, |
|
244 pack = function (self, val) |
|
245 return FloatTypes[SIZEOF_NUMBER]:pack(val) |
|
246 end, |
|
247 } |
102 |
248 |
103 -- Opaque types: |
249 -- Opaque types: |
104 local Number = char(8) |
|
105 local Insn = char(4) |
250 local Insn = char(4) |
106 |
251 |
107 local Struct = Field{ |
252 local Struct = Field{ |
108 unpack = |
253 unpack = |
109 function (self, bytes, ix) |
254 function (self, bytes, ix) |
257 } |
405 } |
258 assert(Function[10].name == "prototypes", |
406 assert(Function[10].name == "prototypes", |
259 "missed the function prototype list") |
407 "missed the function prototype list") |
260 Function[10].type = Function |
408 Function[10].type = Function |
261 |
409 |
262 local Chunk = Struct{ |
410 local Chunk = Field{ |
263 ChunkHeader{name = "header"}, |
411 unpack = |
264 Function{name = "body"} |
412 function (self, bytes, ix) |
|
413 local chunk = {} |
|
414 local header, ix = ChunkHeader:unpack(bytes, ix) |
|
415 assert(header.signature == "\027Lua", "signature check failed") |
|
416 assert(header.version == 81, "version mismatch") |
|
417 assert(header.format == 0, "format mismatch") |
|
418 assert(header.endianness == 0 or |
|
419 header.endianness == 1, "endianness mismatch") |
|
420 assert(IntegralTypes[header.sizeof_int], "int size unsupported") |
|
421 assert(IntegralTypes[header.sizeof_size_t], "size_t size unsupported") |
|
422 assert(header.sizeof_insn == 4, "insn size unsupported") |
|
423 assert(FloatTypes[header.sizeof_Number], "number size unsupported") |
|
424 assert(header.integral_flag == 0, "integral flag mismatch; only floats supported") |
|
425 |
|
426 save() |
|
427 BIG_ENDIAN = header.endianness == 0 |
|
428 SIZEOF_SIZE_T = header.sizeof_size_t |
|
429 SIZEOF_INT = header.sizeof_int |
|
430 SIZEOF_NUMBER = header.sizeof_Number |
|
431 chunk.header = header |
|
432 chunk.body, ix = Function:unpack(bytes, ix) |
|
433 restore() |
|
434 return chunk, ix |
|
435 end, |
|
436 |
|
437 pack = |
|
438 function (self, val) |
|
439 local data |
|
440 save() |
|
441 local header = val.header |
|
442 BIG_ENDIAN = header.endianness == 0 |
|
443 SIZEOF_SIZE_T = header.sizeof_size_t |
|
444 SIZEOF_INT = header.sizeof_int |
|
445 SIZEOF_NUMBER = header.sizeof_Number |
|
446 data = ChunkHeader:pack(val.header) .. Function:pack(val.body) |
|
447 restore() |
|
448 return data |
|
449 end |
265 } |
450 } |
266 |
451 |
267 local function validate(chunk) |
452 local function validate(chunk) |
268 if type(chunk) == "function" then |
453 if type(chunk) == "function" then |
269 return validate(string.dump(chunk)) |
454 return validate(string.dump(chunk)) |