# HG changeset patch # User Matthew Wild # Date 1248661933 -3600 # Node ID 0e34461ab2a6bd5cf38141056cd354fd66708762 # Parent 575e8a530f30d18ec1caa8cac835c083784ca20d Add new debug extension diff -r 575e8a530f30 -r 0e34461ab2a6 Makefile --- a/Makefile Mon Jul 27 03:30:29 2009 +0100 +++ b/Makefile Mon Jul 27 03:32:13 2009 +0100 @@ -1,11 +1,14 @@ -OPTIONS=--with-minify --with-uglify --with-compile --with-virtual-io +OPTIONS=-q --with-minify --with-uglify --with-compile --with-virtual-io squish: squish.lua squishy - ./squish.lua $(OPTIONS) + ./squish.lua $(OPTIONS) # Bootstrap squish + chmod +x squish + ./squish -q debug # Minify debug code + ./squish $(OPTIONS) --with-debug # Build squish with minified debug install: squish install squish /usr/local/bin/squish clean: - rm squish + rm squish squish.debug diff -r 575e8a530f30 -r 0e34461ab2a6 debug/minichunkspy.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debug/minichunkspy.lua Mon Jul 27 03:32:13 2009 +0100 @@ -0,0 +1,294 @@ +-- Minichunkspy: Disassemble and reassemble chunks. +-- Copyright M Joonas Pihlaja 2009 +-- MIT license +-- +-- minichunkspy = require"minichunkspy" +-- +-- chunk = string.dump(loadfile"blabla.lua") +-- disassembled_chunk = minichunkspy.disassemble(chunk) +-- chunk = minichunkspy.assemble(disassembled_chunk) +-- assert(minichunkspy.validate()) +-- +-- Tested on little-endian 32 bit platforms. Modify +-- the Size_t type to be a 64 bit integer to make it work +-- for 64 bit systems, and set BIG_ENDIAN = true for +-- big-endian systems. +local string, table, math = string, table, math +local ipairs, setmetatable, type, assert = ipairs, setmetatable, type, assert +local _ = __END_OF_GLOBALS__ +local string_char, string_byte, string_sub = string.char, string.byte, string.sub +local table_concat = table.concat +local math_abs, math_ldexp, math_frexp = math.abs, math.ldexp, math.frexp +local Inf = math.huge +local Nan = Inf - Inf + +local BIG_ENDIAN = false --twiddle this for your platform. + +local function construct (class, ...) + return class.new(class, ...) +end + +local mt_memo = {} + +local Field = construct{ + new = + function (class, self) + local self = self or {} + local mt = mt_memo[class] or { + __index = class, + __call = construct + } + mt_memo[class] = mt + return setmetatable(self, mt) + end, +} + +local None = Field{ + unpack = function (self, bytes, ix) return nil, ix end, + pack = function (self, val) return "" end +} + +local char_memo = {} + +local function char(n) + local field = char_memo[n] or Field{ + unpack = function (self, bytes, ix) + return string_sub(bytes, ix, ix+n-1), ix+n + end, + pack = function (self, val) return string_sub(val, 1, n) end + } + char_memo[n] = field + return field +end + +local uint8 = Field{ + unpack = function (self, bytes, ix) + return string_byte(bytes, ix, ix), ix+1 + end, + pack = function (self, val) return string_char(val) end +} + +local uint32 = Field{ + unpack = + function (self, bytes, ix) + local a,b,c,d = string_byte(bytes, ix, ix+3) + if BIG_ENDIAN then a,b,c,d = d,c,b,a end + return a + b*256 + c*256^2 + d*256^3, ix+4 + end, + pack = + function (self, val) + assert(type(val) == "number", + "unexpected value type to pack as an uint32") + local a,b,c,d + d = val % 2^32 + a = d % 256; d = (d - a) / 256 + b = d % 256; d = (d - b) / 256 + c = d % 256; d = (d - c) / 256 + if BIG_ENDIAN then a,b,c,d = d,c,b,a end + return string_char(a,b,c,d) + end +} + +local int32 = uint32{ + unpack = function (self, bytes, ix) + local val, ix = uint32:unpack(bytes, ix) + return val < 2^32 and val or (val - 2^31), ix + end +} + +local Byte = uint8 +local Size_t = uint32 +local Integer = int32 + +-- Opaque types: +local Number = char(8) +local Insn = char(4) + +local Struct = Field{ + unpack = + function (self, bytes, ix) + local val = {} + local i,j = 1,1 + while self[i] do + local field = self[i] + local key = field.name + if not key then key, j = j, j+1 end + --print("unpacking struct field", key, " at index ", ix) + val[key], ix = field:unpack(bytes, ix) + i = i+1 + end + return val, ix + end, + pack = + function (self, val) + local data = {} + local i,j = 1,1 + while self[i] do + local field = self[i] + local key = field.name + if not key then key, j = j, j+1 end + data[i] = field:pack(val[key]) + i = i+1 + end + return table_concat(data) + end +} + +local List = Field{ + unpack = + function (self, bytes, ix) + local len, ix = Integer:unpack(bytes, ix) + local vals = {} + local field = self.type + for i=1,len do + --print("unpacking list field", i, " at index ", ix) + vals[i], ix = field:unpack(bytes, ix) + end + return vals, ix + end, + pack = + function (self, vals) + local len = #vals + local data = { Integer:pack(len) } + local field = self.type + for i=1,len do + data[#data+1] = field:pack(vals[i]) + end + return table_concat(data) + end +} + +local Boolean = Field{ + unpack = + function (self, bytes, ix) + local val, ix = Integer:unpack(bytes, ix) + assert(val == 0 or val == 1, + "unpacked an unexpected value "..val.." for a Boolean") + return val == 1, ix + end, + pack = + function (self, val) + assert(type(val) == "boolean", + "unexpected value type to pack as a Boolean") + return Integer:pack(val and 1 or 0) + end +} + +local String = Field{ + unpack = + function (self, bytes, ix) + local len, ix = Integer:unpack(bytes, ix) + local val = nil + if len > 0 then + -- len includes trailing nul byte; ignore it + local string_len = len - 1 + val = bytes:sub(ix, ix+string_len-1) + end + return val, ix + len + end, + pack = + function (self, val) + assert(type(val) == "nil" or type(val) == "string", + "unexpected value type to pack as a String") + if val == nil then + return Integer:pack(0) + end + return Integer:pack(#val+1) .. val .. "\000" + end +} + +local ChunkHeader = Struct{ + char(4){name = "signature"}, + Byte{name = "version"}, + Byte{name = "format"}, + Byte{name = "endianness"}, + Byte{name = "sizeof_int"}, + Byte{name = "sizeof_size_t"}, + Byte{name = "sizeof_insn"}, + Byte{name = "sizeof_Number"}, + Byte{name = "integral_flag"}, +} + +local ConstantTypes = { + [0] = None, + [1] = Boolean, + [3] = Number, + [4] = String, +} +local Constant = Field{ + unpack = + function (self, bytes, ix) + local t, ix = Byte:unpack(bytes, ix) + local field = ConstantTypes[t] + assert(field, "unknown constant type "..t.." to unpack") + local v, ix = field:unpack(bytes, ix) + return { + type = t, + value = v + }, ix + end, + pack = + function (self, val) + local t, v = val.type, val.value + return Byte:pack(t) .. ConstantTypes[t]:pack(v) + end +} + +local Local = Struct{ + String{name = "name"}, + Integer{name = "startpc"}, + Integer{name = "endpc"} +} + +local Function = Struct{ + String{name = "name"}, + Integer{name = "line"}, + Integer{name = "last_line"}, + Byte{name = "num_upvalues"}, + Byte{name = "num_parameters"}, + Byte{name = "is_vararg"}, + Byte{name = "max_stack_size"}, + List{name = "insns", type = Insn}, + List{name = "constants", type = Constant}, + List{name = "prototypes", type = nil}, --patch type below + List{name = "source_lines", type = Integer}, + List{name = "locals", type = Local}, + List{name = "upvalues", type = String}, +} +assert(Function[10].name == "prototypes", + "missed the function prototype list") +Function[10].type = Function + +local Chunk = Struct{ + ChunkHeader{name = "header"}, + Function{name = "body"} +} + +local function validate(chunk) + if type(chunk) == "function" then + return validate(string.dump(chunk)) + end + local f = Chunk:unpack(chunk, 1) + local chunk2 = Chunk:pack(f) + + if chunk == chunk2 then return true end + + local i + local len = math.min(#chunk, #chunk2) + for i=1,len do + local a = chunk:sub(i,i) + local b = chunk:sub(i,i) + if a ~= b then + return false, ("chunk roundtripping failed: ".. + "first byte difference at index %d"):format(i) + end + end + return false, ("chunk round tripping failed: ".. + "original length %d vs. %d"):format(#chunk, #chunk2) +end + +return { + disassemble = function (chunk) return Chunk:unpack(chunk, 1) end, + assemble = function (disassembled) return Chunk:pack(disassembled) end, + validate = validate +} diff -r 575e8a530f30 -r 0e34461ab2a6 debug/squish.debug.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debug/squish.debug.lua Mon Jul 27 03:32:13 2009 +0100 @@ -0,0 +1,22 @@ + +local cs = require "minichunkspy" + +local function ___adjust_chunk(chunk, newname, lineshift) + local c = cs.disassemble(string.dump(chunk)); + c.body.name = newname; + + lineshift = -c.body.line; + local function shiftlines(c) + c.line = c.line + lineshift; + c.last_line = c.last_line + lineshift; + for i, line in ipairs(c.source_lines) do + c.source_lines[i] = line+lineshift; + end + for i, f in ipairs(c.prototypes) do + shiftlines(f); + end + end + shiftlines(c.body); + + return assert(loadstring(cs.assemble(c), newname))(); +end diff -r 575e8a530f30 -r 0e34461ab2a6 debug/squishy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debug/squishy Mon Jul 27 03:32:13 2009 +0100 @@ -0,0 +1,9 @@ +Option "minify-locals" (false) +Option "minify-level" "full" + +Module "minichunkspy" "minichunkspy.lua" + +Main "squish.debug.lua" + +Output "squish.debug" + diff -r 575e8a530f30 -r 0e34461ab2a6 squish.lua --- a/squish.lua Mon Jul 27 03:30:29 2009 +0100 +++ b/squish.lua Mon Jul 27 03:32:13 2009 +0100 @@ -160,30 +160,7 @@ end if enable_debug then - f:write [[ - local function ___rename_chunk(chunk, name) - if type(chunk) == "function" then - chunk = string.dump(chunk); - end - local intsize = chunk:sub(8,8):byte(); - local b = { chunk:sub(13, 13+intsize-1):byte(1, intsize) }; - local oldlen = 0; - for i = 1, #b do - oldlen = oldlen + b[i] * 2^((i-1)*8); - end - - local newname = name.."\0"; - local newlen = #newname; - - local b = { }; - for i=1,intsize do - b[i] = string.char(math.floor(newlen / 2^((i-1)*8)) % (2^(i*8))); - end - - return loadstring(chunk:sub(1, 12)..table.concat(b)..newname - ..chunk:sub(13+intsize+oldlen, -1)); - end - ]]; + f:write(require_resource("squish.debug")); end print_verbose("Packing modules..."); @@ -200,7 +177,7 @@ f:write(data); f:write("end)\n"); if enable_debug then - f:write(string.format("package.preload[%q] = ___rename_chunk(package.preload[%q], %q);\n\n", + f:write(string.format("package.preload[%q] = ___adjust_chunk(package.preload[%q], %q);\n\n", modulename, modulename, "@"..path)); end else diff -r 575e8a530f30 -r 0e34461ab2a6 squishy --- a/squishy Mon Jul 27 03:30:29 2009 +0100 +++ b/squishy Mon Jul 27 03:32:13 2009 +0100 @@ -35,3 +35,7 @@ if GetOption "with-virtual-io" then Resource "vio" "vio/vio.lua" end + +if GetOption "with-debug" then + Resource "squish.debug" "squish.debug" +end