compile/minichunkspy.lua

changeset 57
db9b079c1d33
parent 45
69b3487c71cc
equal deleted inserted replaced
56:791c9d8b750c 57:db9b079c1d33
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)
175 } 320 }
176 321
177 local String = Field{ 322 local String = Field{
178 unpack = 323 unpack =
179 function (self, bytes, ix) 324 function (self, bytes, ix)
180 local len, ix = Integer:unpack(bytes, ix) 325 local len, ix = Size_t:unpack(bytes, ix)
181 local val = nil 326 local val = nil
182 if len > 0 then 327 if len > 0 then
183 -- len includes trailing nul byte; ignore it 328 -- len includes trailing nul byte; ignore it
184 local string_len = len - 1 329 local string_len = len - 1
185 val = bytes:sub(ix, ix+string_len-1) 330 val = bytes:sub(ix, ix+string_len-1)
189 pack = 334 pack =
190 function (self, val) 335 function (self, val)
191 assert(type(val) == "nil" or type(val) == "string", 336 assert(type(val) == "nil" or type(val) == "string",
192 "unexpected value type to pack as a String") 337 "unexpected value type to pack as a String")
193 if val == nil then 338 if val == nil then
194 return Integer:pack(0) 339 return Size_t:pack(0)
195 end 340 end
196 return Integer:pack(#val+1) .. val .. "\000" 341 return Size_t:pack(#val+1) .. val .. "\000"
197 end 342 end
198 } 343 }
199 344
200 local ChunkHeader = Struct{ 345 local ChunkHeader = Struct{
201 char(4){name = "signature"}, 346 char(4){name = "signature"},
220 function (self, bytes, ix) 365 function (self, bytes, ix)
221 local t, ix = Byte:unpack(bytes, ix) 366 local t, ix = Byte:unpack(bytes, ix)
222 local field = ConstantTypes[t] 367 local field = ConstantTypes[t]
223 assert(field, "unknown constant type "..t.." to unpack") 368 assert(field, "unknown constant type "..t.." to unpack")
224 local v, ix = field:unpack(bytes, ix) 369 local v, ix = field:unpack(bytes, ix)
370 if t == 3 then
371 assert(type(v) == "number")
372 end
225 return { 373 return {
226 type = t, 374 type = t,
227 value = v 375 value = v
228 }, ix 376 }, ix
229 end, 377 end,
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))

mercurial