ljx

FORK: LuaJIT with native 5.2 and 5.3 support
git clone https://git.neptards.moe/neptards/ljx.git
Log | Files | Refs | README

bc.lua (6448B)


      1 ----------------------------------------------------------------------------
      2 -- LuaJIT bytecode listing module.
      3 --
      4 -- Copyright (C) 2005-2016 Mike Pall. All rights reserved.
      5 -- Released under the MIT license. See Copyright Notice in luajit.h
      6 ----------------------------------------------------------------------------
      7 --
      8 -- This module lists the bytecode of a Lua function. If it's loaded by -jbc
      9 -- it hooks into the parser and lists all functions of a chunk as they
     10 -- are parsed.
     11 --
     12 -- Example usage:
     13 --
     14 --   luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)'
     15 --   luajit -jbc=- foo.lua
     16 --   luajit -jbc=foo.list foo.lua
     17 --
     18 -- Default output is to stderr. To redirect the output to a file, pass a
     19 -- filename as an argument (use '-' for stdout) or set the environment
     20 -- variable LUAJIT_LISTFILE. The file is overwritten every time the module
     21 -- is started.
     22 --
     23 -- This module can also be used programmatically:
     24 --
     25 --   local bc = require("jit.bc")
     26 --
     27 --   local function foo() print("hello") end
     28 --
     29 --   bc.dump(foo)           --> -- BYTECODE -- [...]
     30 --   print(bc.line(foo, 2)) --> 0002    KSTR     1   1      ; "hello"
     31 --
     32 --   local out = {
     33 --     -- Do something with each line:
     34 --     write = function(t, ...) io.write(...) end,
     35 --     close = function(t) end,
     36 --     flush = function(t) end,
     37 --   }
     38 --   bc.dump(foo, out)
     39 --
     40 ------------------------------------------------------------------------------
     41 
     42 -- Cache some library functions and objects.
     43 local jit = require("jit")
     44 assert(jit.version_num == 20100, "LuaJIT core/library version mismatch")
     45 local jutil = require("jit.util")
     46 local vmdef = require("jit.vmdef")
     47 local bit = require("bit")
     48 local sub, gsub, format = string.sub, string.gsub, string.format
     49 local byte, band, shr = string.byte, bit.band, bit.rshift
     50 local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck
     51 local funcuvname = jutil.funcuvname
     52 local bcnames = vmdef.bcnames
     53 local stdout, stderr = io.stdout, io.stderr
     54 
     55 ------------------------------------------------------------------------------
     56 
     57 local function ctlsub(c)
     58   if c == "\n" then return "\\n"
     59   elseif c == "\r" then return "\\r"
     60   elseif c == "\t" then return "\\t"
     61   else return format("\\%03d", byte(c))
     62   end
     63 end
     64 
     65 -- Return one bytecode line.
     66 local function bcline(func, pc, prefix)
     67   local ins, m = funcbc(func, pc)
     68   if not ins then return end
     69   local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128)
     70   local a = band(shr(ins, 8), 0xff)
     71   local oidx = 6*band(ins, 0xff)
     72   local op = sub(bcnames, oidx+1, oidx+6)
     73   local s = format("%04d %s %-6s %3s ",
     74     pc, prefix or "  ", op, ma == 0 and "" or a)
     75   local d = shr(ins, 16)
     76   if mc == 13*128 then -- BCMjump
     77     return format("%s=> %04d\n", s, pc+d-0x7fff)
     78   end
     79   if mb ~= 0 then
     80     d = band(d, 0xff)
     81   elseif mc == 0 then
     82     return s.."\n"
     83   end
     84   local kc
     85   if mc == 10*128 then -- BCMstr
     86     kc = funck(func, -d-1)
     87     kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
     88   elseif mc == 9*128 then -- BCMnum
     89     kc = funck(func, d)
     90     if op == "TSETM " then kc = kc - 2^52 end
     91   elseif mc == 12*128 then -- BCMfunc
     92     local fi = funcinfo(funck(func, -d-1))
     93     if fi.ffid then
     94       kc = vmdef.ffnames[fi.ffid]
     95     else
     96       kc = fi.loc
     97     end
     98   elseif mc == 5*128 then -- BCMuv
     99     kc = funcuvname(func, d)
    100   end
    101   if ma == 5 then -- BCMuv
    102     local ka = funcuvname(func, a)
    103     if kc then kc = ka.." ; "..kc else kc = ka end
    104   end
    105   if mb ~= 0 then
    106     local b = shr(ins, 24)
    107     if kc then return format("%s%3d %3d  ; %s\n", s, b, d, kc) end
    108     return format("%s%3d %3d\n", s, b, d)
    109   end
    110   if kc then return format("%s%3d      ; %s\n", s, d, kc) end
    111   if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits
    112   return format("%s%3d\n", s, d)
    113 end
    114 
    115 -- Collect branch targets of a function.
    116 local function bctargets(func)
    117   local target = {}
    118   for pc=1,1000000000 do
    119     local ins, m = funcbc(func, pc)
    120     if not ins then break end
    121     if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end
    122   end
    123   return target
    124 end
    125 
    126 -- Dump bytecode instructions of a function.
    127 local function bcdump(func, out, all, prefix)
    128   prefix = prefix or ""
    129   if not out then out = stdout end
    130   local fi = funcinfo(func)
    131   if all and fi.children then
    132     for n=-1,-1000000000,-1 do
    133       local k = funck(func, n)
    134       if not k then break end
    135       if type(k) == "proto" then bcdump(k, out, true) end
    136     end
    137   end
    138   out:write(format(prefix.."+- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined))
    139   for i=1,(#fi.uvinit) do
    140     local uvv = band(fi.uvinit[i],0xfff) -- ORDER PROTO_UV_* in lj_obj.h
    141     local loc = band(fi.uvinit[i],0x8000)~=0 and " PARENT" or ""
    142     local imu = band(fi.uvinit[i],0x4000)~=0 and " IMMUTABLE" or ""
    143     local env = band(fi.uvinit[i],0x2000)~=0 and " ENV" or ""
    144     local clo = band(fi.uvinit[i],0x1000)~=0 and " CLOSURE" or ""
    145     out:write((prefix.."| UV@%d = %d%s%s%s%s %04x\n"):format(i-1,uvv,loc,imu,env,clo,fi.uvinit[i]))
    146     if clo ~= "" then
    147       local k = funck(func, -band(fi.uvinit[i],0xfff)-1)
    148       out:write(prefix .. "|   |\n")
    149       if k then
    150         bcdump(k, out, nil, prefix .. "|   ")
    151       else
    152         print ("cant load k",-band(fi.uvinit[i],0xfff)-1)
    153       end
    154     end
    155   end
    156   out:write(prefix.."+-----------\n")
    157 
    158   local target = bctargets(func)
    159   for pc=1,1000000000 do
    160     local s = bcline(func, pc, target[pc] and "=>")
    161     if not s then break end
    162     out:write(prefix .. s)
    163   end
    164   out:write(prefix.."\n")
    165   out:flush()
    166 end
    167 
    168 ------------------------------------------------------------------------------
    169 
    170 -- Active flag and output file handle.
    171 local active, out
    172 
    173 -- List handler.
    174 local function h_list(func)
    175   return bcdump(func, out)
    176 end
    177 
    178 -- Detach list handler.
    179 local function bclistoff()
    180   if active then
    181     active = false
    182     jit.attach(h_list)
    183     if out and out ~= stdout and out ~= stderr then out:close() end
    184     out = nil
    185   end
    186 end
    187 
    188 -- Open the output file and attach list handler.
    189 local function bcliston(outfile)
    190   if active then bclistoff() end
    191   if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end
    192   if outfile then
    193     out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
    194   else
    195     out = stderr
    196   end
    197   jit.attach(h_list, "bc")
    198   active = true
    199 end
    200 
    201 -- Public module functions.
    202 return {
    203   line = bcline,
    204   dump = bcdump,
    205   targets = bctargets,
    206   on = bcliston,
    207   off = bclistoff,
    208   start = bcliston -- For -j command line option.
    209 }
    210