[
  {
    "path": ".github/workflows/semgrep.yml",
    "content": "\non:\n  pull_request: {}\n  workflow_dispatch: {}\n  push: \n    branches:\n      - main\n      - master\n  schedule:\n    - cron: '0 0 * * *'\nname: Semgrep config\njobs:\n  semgrep:\n    name: semgrep/ci\n    runs-on: ubuntu-20.04\n    env:\n      SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}\n      SEMGREP_URL: https://cloudflare.semgrep.dev\n      SEMGREP_APP_URL: https://cloudflare.semgrep.dev\n      SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version\n    container:\n      image: returntocorp/semgrep\n    steps:\n      - uses: actions/checkout@v3\n      - run: semgrep ci\n"
  },
  {
    "path": "COPYRIGHT",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016-2017 CloudFlare\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "LOOM\n====\n\nIt's a replacement / enhancement of the `-jdump` option included in LuaJIT.\n\nAs a command line argument\n===\n\nJust put it in a `jit/` directory within `package.path` or `$LUA_PATH`, typically `'/usr/local/share/luajit-2.1..../jit/'`; but it also works in `'/usr/local/share/lua/5.1/jit/'` or even `'./jit/'`.  Then it can be used as an argument to LuaJIT in the form:\n\n**`-jloom[=<tmpl>[,<out>]]`**\n\n`<tmpl>` is a template file (default `'loom.html'`) and `<out>` is an output file name (default `io.stdout`).\n\nLua API\n===\n\nIf you want to report traces on just part of your code, it's better to use it explicitly.\n\n**`local loom = require 'jit.loom'`**\n\nAs any module, you have to `require()` it first.\n\n**`loom.on()`**\n\nStarts recording all JIT events and traces.\n\n**`traces, funcs = loom.off()`**\n\n**`report = loom.off([f [, ...]])`**\n\nStops recording and performs any processing and cross references needed to actually generate a report.\n\nCalled without any arguments, returns two Lua tables, one with the processed trace information and a second one with all the functions involved in those traces execution.\n\nThe second form is equivalent to\n\n    do\n        local traces, funcs = loom.off()\n        report = f(traces, funcs, ...)\n    end\n\nThat is, both return values (the `traces` and `funcs` arrays) are passed to the given function `f`, together with any extra argument, and returns any return value(s) of `f`.\n\n**`loom.start(tmpl, out)`**\n\nImplements the `-jloom[=tmpl[,out]]` option. The `tmpl` argument is passed to `loom.template()` to create a reporting function.  If omitted, defaults to `'loom.html'`.  The `out` parameter is either a writeable open file or a file name where the report is written into (after formatting by the template), defaults to `io.stdout`.  When the Lua VM is terminated normally, `loom.off()` is called with the reporting function created by the given template.,\n\n### Utility Functions\n\nThere are some functions included in the `loom` package to help formatting a report.\n\n**`f = loom.template(tmpl)`**\n\nThe string `tmpl` is a report template using the template syntax described below.  If it doesn't contain any line break, is interpreted as a pathname to read the template from a text file.\n\nThe template is compiled into a Lua function that takes some arguments (named with `{@ name ...}` tags) and outputs the result as a string.\n\n\n**`loom.annotated(funcs, traces)`**\n\nReturns an annotated listing of the source code of the given `funcs` and `traces` arrays.\n\n**`loom.allipairs(t)`**\n\nLike `ipairs(t)`, but stops at `table.maxn(t)` instead of the first `nil` value.\n\n**`loom.sortedpairs(t)`**\n\nReturns an iterator that visits the same pairs as `pairs(t)`, but sorted by keys.\n\n\nTemplate syntax\n===\n\nThe included template implementation is based on Danila Poyarkov's [lua-template](https://github.com/dannote/lua-template), with a syntax more like Django's or Handlebar's, to make it more friendly to editors that help with HTML content.\n\n\n**`{% lua code %}`**\n\nEmbeds any Lua code\n\n**`{{ expression }}`**\n\nOutputs the result of the Lua expression, with the `&`, `\"`, `<` and `>` characters escaped.\n\n**`{{= expression }}`**\n\nOutputs the result of the Lua expression verbatim, without any character escaping.\n\n**`{{: 'fmt', args, ... }}`**\n\nOutputs the result of `string.format(fmt, args, ...)` without any escaping.\n\n**`{@ name ... }`**\n\nDefines template argument names.  Each `name` must be a valid Lua variable name (that is, a sequence of letters, numbers or underscores not beginning with a number), separated by commas or spaces (or any non-alfanumeric-underscore character).\n\n\nIncluded Template\n===\n\nThe included `loom.html` template renders the trace report as an HTML document.  It's divided in two sections: a Sourcecode -> Bytecode -> Traces one, and a list of traces, with the Bytecode -> IR -> mcode progression for each one.\n\n1.- Source list\n---\n\nFor each function that appears in the traces, the source code is shown on the left column (if the source was in a file) and the bytecode at right of it with a random background color (a different one for each function).\n\nThe bytecode is shown in the order it appears in memory, so some source lines are in a different order as in the original source or repeated, for example the opening of a `for` loop appears again (in a lighter gray) in the bottom of the loop.\n\nAt the right of the bytecode, there are links of the form `<tr>/<seq>`, showing some trace executed it at some sequential number.  For example `3/4` means it's the fourth bytecode executed by trace #3.  A single bytecode can appear several times in the same trace (for example, when unrolling a loop).  Each trace have assigned a random background color.\n\nTrace start and abort events are also marked to the right of the relevant bytecode, with `[n=xx]` notes telling how many times they happened until the trace gets compiled or blacklisted.\n\n2.- Trace list\n---\n\nEach trace gets a three-column table framed on the same color as the respective links in the previous section.\n\nThe first column lists the bytecode in the order it was executed.  On the left there are links that go back to the same bytecode on the previous section.  The bytecodes keep the same background colour of the function.  Clicking on the column title toggles source code annotations.\n\nThe second column lists the IR code generated by the trace.  While the total semantics is supposed to be maintained, there's no direct correspondence between IR and bytecode instructions.  For example, as much code as possible is moved before the start of an inner loop, and compilable library functions are just checked (to assure they're the right functions) and IR code is emitted instead of a call to the function.\n\nSnapshot points are inserted in the IR code, but the snapshot content isn't shown by default.  To see them, either hover the mouse over the snapshot or click in the column title to reveal all at the same time.\n\nThe third column is the generated mcode that is natively executed by the processor.  Exit points are labelled by the snapshot number or a trace number if a later trace patched itself in.\n\nExamples\n===\n\nThe 'sample.lua' file includes some small code snippets to play with.  For example, the comments about `-jv` option show:\n\n    luajit -jv -e \"for i=1,1000 do for j=1,1000 do end end\"\n\nTo output just two lines (one per trace).  Changing to `-jdump` results in:\n\n    ---- TRACE 1 start (command line):1\n    0009  FORL     4 => 0009\n    ---- TRACE 1 IR\n    0001    int SLOAD  #5    CI\n    0002  + int ADD    0001  +1\n    0003 >  int LE     0002  +1000\n    0004 ------ LOOP ------------\n    0005  + int ADD    0002  +1\n    0006 >  int LE     0005  +1000\n    0007    int PHI    0002  0005\n    ---- TRACE 1 mcode 47\n    0bcbffd1  mov dword [0x41a94410], 0x1\n    0bcbffdc  cvttsd2si ebp, [rdx+0x20]\n    0bcbffe1  add ebp, +0x01\n    0bcbffe4  cmp ebp, 0x3e8\n    0bcbffea  jg 0x0bcb0014\t->1\n    ->LOOP:\n    0bcbfff0  add ebp, +0x01\n    0bcbfff3  cmp ebp, 0x3e8\n    0bcbfff9  jle 0x0bcbfff0\t->LOOP\n    0bcbfffb  jmp 0x0bcb001c\t->3\n    ---- TRACE 1 stop -> loop\n\n    ---- TRACE 2 start 1/3 (command line):1\n    0010  FORL     0 => 0005\n    0005  KSHORT   4   1\n    0006  KSHORT   5 1000\n    0007  KSHORT   6   1\n    0008  JFORI    4 => 0010\n    ---- TRACE 2 IR\n    0001    num SLOAD  #1    I\n    0002    num ADD    0001  +1\n    0003 >  num LE     0002  +1000\n    ---- TRACE 2 mcode 81\n    0bcbff79  mov dword [0x41a94410], 0x2\n    0bcbff84  movsd xmm6, [0x403382b8]\n    0bcbff8d  movsd xmm5, [0x403382c8]\n    0bcbff96  movsd xmm7, [rdx]\n    0bcbff9a  addsd xmm7, xmm6\n    0bcbff9e  ucomisd xmm5, xmm7\n    0bcbffa2  jb 0x0bcb0014\t->1\n    0bcbffa8  movsd [rdx+0x38], xmm6\n    0bcbffad  movsd [rdx+0x30], xmm6\n    0bcbffb2  movsd [rdx+0x28], xmm5\n    0bcbffb7  movsd [rdx+0x20], xmm6\n    0bcbffbc  movsd [rdx+0x18], xmm7\n    0bcbffc1  movsd [rdx], xmm7\n    0bcbffc5  jmp 0x0bcbffd1\n    ---- TRACE 2 stop -> 1\n\nTo recreate under loom, try:\n\n    luajit -jloom -e \"require('sample').lulu()\" > out.html\n\nAnd open the resulting `out.html` with a browser to see the same thing with nice colours and links to help following how the traces flow together.\n\n![screenshot](shot.png)\n\n"
  },
  {
    "path": "jit/loom.lua",
    "content": "\nlocal bit = require 'bit'\nlocal jutil = require 'jit.util'\nlocal vmdef = require 'jit.vmdef'\nlocal bc = require 'jit.bc'\nlocal disass = require('jit.dis_'..jit.arch)\n\nlocal band, shr = bit.band, bit.rshift\nlocal inf = tonumber('inf')\n\n\nlocal function pushf(t, f, ...)\n\tif select('#', ...) > 0 then\n\t\tf = f:format(...)\n\tend\n\tt[#t+1] = f\n\treturn f\nend\n\nlocal function allipairs(t, start)\n\tstart = start or 1\n\tlocal maxn = table.maxn(t)\n\treturn function (t, k)\t\t\t-- luacheck: ignore t\n\t\trepeat\n\t\t\tk = k + 1\n\t\tuntil t[k] ~= nil or k > maxn\n\t\treturn k <= maxn and k or nil, t[k]\n\tend, t, start-1\nend\n\nlocal function sortedpairs(t, emptyelem)\n\tif emptyelem ~= nil and next(t) == nil then\n\t\tlocal done = false\n\t\treturn function ()\n\t\t\tif not done then\n\t\t\t\tdone = true\n\t\t\t\treturn emptyelem\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal t2, map = {}, {}\n\tfor k in pairs(t) do\n\t\tlocal sk = type(k) == 'number' and ('%20.6f'):format(k) or tostring(k)\n\t\tt2[#t2+1] = sk\n\t\tmap[sk] = k\n\tend\n\ttable.sort(t2)\n\tlocal i = 1\n\treturn function ()\n\t\tlocal k = map[t2[i]]\n\t\ti = i+1\n\t\treturn k, t[k]\n\tend\nend\n\n-- copied from jit.dump\n\nlocal function fmtfunc(func, pc)\n\tlocal fi = jutil.funcinfo(func, pc)\n\tif fi.loc then\n\t\treturn fi.loc\n\telseif fi.ffid then\n\t\treturn vmdef.ffnames[fi.ffid]\n\telseif fi.addr then\n\t\treturn (\"C:%x\"):format(fi.addr)\n\telse\n\t\treturn \"(?)\"\n\tend\nend\n\n-----------\n\nlocal function bcline(func, pc, prefix)\n\tlocal l\n\tif pc >= 0 then\n\t\tl = bc.line(func, pc, prefix)\n\t\tif not l then return l end\n\telse\n\t\tl = \"0000 \"..prefix..\" FUNCC      \\n\"\n\tend\n\n\tl = l:gsub('%s+$', '')\n\treturn l\nend\n\n\nlocal function func_bc(func, o)\n\to = o or {}\n\to[func] = jutil.funcinfo(func)\n\tif o[func].children then\n\t\tfor n = -1, -inf, -1 do\n\t\t\tlocal k = jutil.funck(func, n)\n\t\t\tif not k then break end\n\t\t\tif type(k) == 'proto' then func_bc(k, o) end\n\t\tend\n\tend\n\to[func].func = func\n\to[func].bytecode = {}\n\tif not o[func].addr then\n\t\tlocal target = bc.targets(func)\n\t\tfor pc = 1, inf do\n\t\t\tlocal s = bcline (func, pc, target[pc] and \"=>\")\n\t\t\tif not s then break end\n\t\t\tlocal fi_sub = jutil.funcinfo(func, pc)\n\t\t\to[func].bytecode[pc] = {fi_sub.currentline, s}\n\t\tend\n\tend\n\treturn o\nend\n\n--------------------------------------\n-- tracing\n\n-- copied from jit/dump.lua\n\nlocal symtabmt = { __index = false }\nlocal symtab = {}\nlocal nexitsym = 0\n\n-- Fill nested symbol table with per-trace exit stub addresses.\nlocal function fillsymtab_tr(tr, nexit)\n\tlocal t = {}\n\tsymtabmt.__index = t\n\tif jit.arch == \"mips\" or jit.arch == \"mipsel\" then\n\t\tt[jutil.traceexitstub(tr, 0)] = \"exit\"\n\t\treturn\n\tend\n\tfor i=0,nexit-1 do\n\t\tlocal addr = jutil.traceexitstub(tr, i)\n\t\tif addr < 0 then addr = addr + 2^32 end\n\t\tt[addr] = tostring(i)\n\tend\n\tlocal addr = jutil.traceexitstub(tr, nexit)\n\tif addr then t[addr] = \"stack_check\" end\nend\n\n-- Fill symbol table with trace exit stub addresses.\nlocal function fillsymtab(tr, nexit)\n\tlocal t = symtab\n\tif nexitsym == 0 then\n\t\tlocal ircall = vmdef.ircall\n\t\tfor i=0,#ircall do\n\t\t\tlocal addr = jutil.ircalladdr(i)\n\t\t\tif addr and addr ~= 0 then\n\t\t\t\tif addr < 0 then addr = addr + 2^32 end\n\t\t\t\tt[addr] = ircall[i]\n\t\t\tend\n\t\tend\n\tend\n\tif nexitsym == 1000000 then -- Per-trace exit stubs.\n\t\tfillsymtab_tr(tr, nexit)\n\telseif nexit > nexitsym then -- Shared exit stubs.\n\t\tfor i=nexitsym,nexit-1 do\n\t\t\tlocal addr = jutil.traceexitstub(i)\n\t\t\tif addr == nil then -- Fall back to per-trace exit stubs.\n\t\t\t\tfillsymtab_tr(tr, nexit)\n\t\t\t\tsetmetatable(symtab, symtabmt)\n\t\t\t\tnexit = 1000000\n\t\t\t\tbreak\n\t\t\tend\n\t\t\tif addr < 0 then addr = addr + 2^32 end\n\t\t\tt[addr] = tostring(i)\n\t\tend\n\t\tnexitsym = nexit\n\tend\n\treturn t\nend\n\n-- Disassemble machine code.\nlocal function dump_mcode(tr)\n\tlocal o = {}\n\tlocal info = jutil.traceinfo(tr)\n\tif not info then return end\n\tlocal mcode, addr, loop = jutil.tracemc(tr)\n\tif not mcode then return end\n\tif addr < 0 then addr = addr + 2^32 end\n\tlocal ctx = disass.create(mcode, addr, function (s) pushf(o, s) end)\n\tctx.hexdump = 0\n\tctx.symtab = fillsymtab(tr, info.nexit)\n\tif loop ~= 0 then\n\t\tsymtab[addr+loop] = \"LOOP\"\n\t\tctx:disass(0, loop)\n\t\tpushf (o, \"->LOOP:\\n\")\n\t\tctx:disass(loop, #mcode-loop)\n\t\tsymtab[addr+loop] = nil\n\telse\n\t\tctx:disass(0, #mcode)\n\tend\n\treturn table.concat(o)\nend\n\n\n\n\nlocal irtype = {\n  [0] = \"nil\",\n  \"fal\",\n  \"tru\",\n  \"lud\",\n  \"str\",\n  \"p32\",\n  \"thr\",\n  \"pro\",\n  \"fun\",\n  \"p64\",\n  \"cdt\",\n  \"tab\",\n  \"udt\",\n  \"flt\",\n  \"num\",\n  \"i8 \",\n  \"u8 \",\n  \"i16\",\n  \"u16\",\n  \"int\",\n  \"u32\",\n  \"i64\",\n  \"u64\",\n  \"sfp\",\n}\n\n-- Lookup tables to convert some literals into names.\nlocal litname = {\n\t[\"SLOAD \"] = setmetatable({}, { __index = function(t, mode)\n\t\tlocal s = \"\"\n\t\tif band(mode, 1) ~= 0 then s = s..\"P\" end\n\t\tif band(mode, 2) ~= 0 then s = s..\"F\" end\n\t\tif band(mode, 4) ~= 0 then s = s..\"T\" end\n\t\tif band(mode, 8) ~= 0 then s = s..\"C\" end\n\t\tif band(mode, 16) ~= 0 then s = s..\"R\" end\n\t\tif band(mode, 32) ~= 0 then s = s..\"I\" end\n\t\tt[mode] = s\n\t\treturn s\n\tend}),\n\t[\"XLOAD \"] = { [0] = \"\", \"R\", \"V\", \"RV\", \"U\", \"RU\", \"VU\", \"RVU\", },\n\t[\"CONV  \"] = setmetatable({}, { __index = function(t, mode)\n\t\tlocal s = irtype[band(mode, 31)]\n\t\ts = irtype[band(shr(mode, 5), 31)]..\".\"..s\n\t\tif band(mode, 0x800) ~= 0 then s = s..\" sext\" end\n\t\tlocal c = shr(mode, 14)\n\t\tif c == 2 then s = s..\" index\" elseif c == 3 then s = s..\" check\" end\n\t\tt[mode] = s\n\t\treturn s\n\tend}),\n\t[\"FLOAD \"] = vmdef.irfield,\n\t[\"FREF  \"] = vmdef.irfield,\n\t[\"FPMATH\"] = vmdef.irfpm,\n\t[\"BUFHDR\"] = { [0] = \"RESET\", \"APPEND\" },\n\t[\"TOSTR \"] = { [0] = \"INT\", \"NUM\", \"CHAR\" },\n}\n\n\nlocal function ctlsub(c)\n\tif c == \"\\n\" then return \"\\\\n\"\n\telseif c == \"\\r\" then return \"\\\\r\"\n\telseif c == \"\\t\" then return \"\\\\t\"\n\telse return (\"\\\\%03d\"):format(c:byte())\n\tend\nend\n\nlocal function formatk(tr, idx)\n\tlocal k, t, slot = jutil.tracek(tr, idx)\n\tlocal tn = type(k)\n\tlocal s\n\tif tn == \"number\" then\n\t\tif k == 2^52+2^51 then\n\t\t\ts = \"bias\"\n\t\telse\n\t\t\ts = (\"%+.14g\"):format(k)\n\t\tend\n\telseif tn == \"string\" then\n\t\ts = (#k > 20 and '\"%.20s\"~' or '\"%s\"'):format(k:gsub(\"%c\", ctlsub))\n\telseif tn == \"function\" then\n\t\ts = fmtfunc(k)\n\telseif tn == \"table\" then\n\t\ts = (\"{%p}\"):format(k)\n\telseif tn == \"userdata\" then\n\t\tif t == 12 then\n\t\t\ts = (\"userdata:%p\"):format(k)\n\t\telse\n\t\t\ts = (\"[%p]\"):format(k)\n\t\t\tif s == \"[0x00000000]\" then s = \"NULL\" end\n\t\tend\n\telseif t == 21 then -- int64_t\n\t\ts = tostring(k):sub(1, -3)\n\t\tif (s):sub(1, 1) ~= \"-\" then s = \"+\"..s end\n\telse\n\t\ts = tostring(k) -- For primitives.\n\tend\n\ts = (\"%-4s\"):format(s)\n\tif slot then\n\t\ts = (\"%s @%d\"):format(s, slot)\n\tend\n\treturn s\nend\n\nlocal function printsnap(tr, snap)\n\tlocal o = {}\n\tlocal n = 2\n\tfor s=0,snap[1]-1 do\n\t\tlocal sn = snap[n]\n\t\tif shr(sn, 24) == s then\n\t\t\tn = n + 1\n\t\t\tlocal ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS\n\t\t\tif ref < 0 then\n\t\t\t\tpushf(o, formatk(tr, ref))\n\t\t\telseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM\n\t\t\t\tpushf(o, \"%04d/%04d\", ref, ref+1)\n\t\t\telse\n\t\t\t\tpushf(o, \"%04d\", ref)\n\t\t\tend\n\t\t\tpushf(o, band(sn, 0x10000) == 0 and \" \" or \"|\") -- SNAP_FRAME\n\t\telse\n\t\t\tpushf(o, \"---- \")\n\t\tend\n\tend\n\tpushf(o, \"]\\n\")\n\treturn table.concat(o)\nend\n\n-- Dump snapshots (not interleaved with IR).\nlocal function dump_snap(tr)\n\tlocal o = {\"---- TRACE \"..tr..\" snapshots\\n\"}\n\tfor i=0,1000000000 do\n\t\tlocal snap = jutil.tracesnap(tr, i)\n\t\tif not snap then break end\n\t\tpushf(o, \"#%-3d %04d [ \", i, snap[0])\n\t\tpushf(o, printsnap(tr, snap))\n\tend\n\treturn table.concat(o)\nend\n\n-- Return a register name or stack slot for a rid/sp location.\nlocal function ridsp_name(ridsp, ins)\n\tlocal rid, slot = band(ridsp, 0xff), shr(ridsp, 8)\n\tif rid == 253 or rid == 254 then\n\t\treturn (slot == 0 or slot == 255) and \" {sink\" or (\" {%04d\"):format(ins-slot)\n\tend\n\tif ridsp > 255 then return (\"[%x]\"):format(slot*4) end\n\tif rid < 128 then return disass.regname(rid) end\n\treturn \"\"\nend\n\n-- Dump CALL* function ref and return optional ctype.\nlocal function dumpcallfunc(o, tr, ins)\n\tlocal ctype\n\tif ins > 0 then\n\t\tlocal m, ot, op1, op2 = jutil.traceir(tr, ins)\t\t-- luacheck: ignore m\n\t\tif band(ot, 31) == 0 then -- nil type means CARG(func, ctype).\n\t\t\tins = op1\n\t\t\tctype = formatk(tr, op2)\n\t\tend\n\tend\n\tif ins < 0 then\n\t\tpushf(o, \"[0x%x](\", tonumber((jutil.tracek(tr, ins))))\n\telse\n\t\tpushf(o, \"%04d (\", ins)\n\tend\n\treturn ctype\nend\n\n-- Recursively gather CALL* args and dump them.\nlocal function dumpcallargs(o, tr, ins)\n\tif ins < 0 then\n\t\tpushf(o, formatk(tr, ins))\n\telse\n\t\tlocal m, ot, op1, op2 = jutil.traceir(tr, ins)\t\t-- luacheck: ignore m\n\t\tlocal oidx = 6*shr(ot, 8)\n\t\tlocal op = vmdef.irnames:sub(oidx+1, oidx+6)\n\t\tif op == \"CARG  \" then\n\t\t\tdumpcallargs(o, tr, op1)\n\t\t\tif op2 < 0 then\n\t\t\t\tpushf(o, \" \"..formatk(tr, op2))\n\t\t\telse\n\t\t\t\tpushf(o, \" %04d\", op2)\n\t\t\tend\n\t\telse\n\t\t\tpushf(o, \"%04d\", ins)\n\t\tend\n\tend\nend\n\n-- Dump IR and interleaved snapshots.\nlocal function dump_ir(tr)\n\tlocal dumpsnap, dumpreg = true, true\n\tlocal info = jutil.traceinfo(tr)\n\tif not info then return end\n\tlocal nins = info.nins\n\tlocal o = {}\n\tlocal irnames = vmdef.irnames\n\tlocal snapref = 65536\n\tlocal snap, snapno\n\tif dumpsnap then\n\t\tsnap = jutil.tracesnap(tr, 0)\n\t\tsnapref = snap[0]\n\t\tsnapno = 0\n\tend\n\tfor ins=1,nins do\n\t\tif ins >= snapref then\n\t\t\tif dumpreg then\n\t\t\t\tpushf (o, \"....              SNAP   #%-3d  [ \", snapno)\n\t\t\telse\n\t\t\t\tpushf (o, \"....        SNAP   #%-3d  [ \", snapno)\n\t\t\tend\n\t\t\tpushf (o, printsnap(tr, snap))\n\t\t\tsnapno = snapno + 1\n\t\t\tsnap = jutil.tracesnap(tr, snapno)\n\t\t\tsnapref = snap and snap[0] or 65536\n\t\tend\n\t\tlocal m, ot, op1, op2, ridsp = jutil.traceir(tr, ins)\n\t\tlocal oidx, t = 6*shr(ot, 8), band(ot, 31)\n\t\tlocal op = irnames:sub(oidx+1, oidx+6)\n\t\tif op == \"LOOP  \" then\n\t\t\tif dumpreg then\n\t\t\t\tpushf (o, \"%04d ------------ LOOP ------------\\n\", ins)\n\t\t\telse\n\t\t\t\tpushf (o, \"%04d ------ LOOP ------------\\n\", ins)\n\t\t\tend\n\t\telseif op ~= \"NOP   \" and op ~= \"CARG  \" and\n\t\t\t(dumpreg or op ~= \"RENAME\")\n\t\tthen\n\t\t\tlocal rid = band(ridsp, 255)\n\t\t\tif dumpreg then\n\t\t\t\tpushf (o, \"%04d %-6s\", ins, ridsp_name(ridsp, ins))\n\t\t\telse\n\t\t\t\tpushf (o, \"%04d \", ins)\n\t\t\tend\n\t\t\tpushf (o, \"%s%s %s %s \",\n\t\t\t\t\t(rid == 254 or rid == 253) and \"}\" or\n\t\t\t\t\t(band(ot, 128) == 0 and \" \" or \">\"),\n\t\t\t\t\tband(ot, 64) == 0 and \" \" or \"+\",\n\t\t\t\t\tirtype[t], op)\n\t\t\tlocal m1, m2 = band(m, 3), band(m, 3*4)\n\t\t\tif op:sub(1, 4) == \"CALL\" then\n\t\t\t\tlocal ctype\n\t\t\t\tif m2 == 1*4 then -- op2 == IRMlit\n\t\t\t\t\tpushf (o, \"%-10s  (\", vmdef.ircall[op2])\n\t\t\t\telse\n\t\t\t\t\tctype = dumpcallfunc(o, tr, op2)\n\t\t\t\tend\n\t\t\t\tif op1 ~= -1 then dumpcallargs(o, tr, op1) end\n\t\t\t\tpushf(o, \")\")\n\t\t\t\tif ctype then pushf(o, \" ctype \"..ctype) end\n\t\t\telseif op == \"CNEW  \" and op2 == -1 then\n\t\t\t\tpushf(o, formatk(tr, op1))\n\t\t\telseif m1 ~= 3 then -- op1 != IRMnone\n\t\t\t\tif op1 < 0 then\n\t\t\t\t\tpushf(o, formatk(tr, op1))\n\t\t\t\telse\n\t\t\t\t\tpushf(o, m1 == 0 and \"%04d\" or \"#%-3d\", op1)\n\t\t\t\tend\n\t\t\t\tif m2 ~= 3*4 then -- op2 != IRMnone\n\t\t\t\t\tif m2 == 1*4 then -- op2 == IRMlit\n\t\t\t\t\t\tlocal litn = litname[op]\n\t\t\t\t\t\tif litn and litn[op2] then\n\t\t\t\t\t\t\tpushf(o, \"  \"..litn[op2])\n\t\t\t\t\t\telseif op == \"UREFO \" or op == \"UREFC \" then\n\t\t\t\t\t\t\tpushf (o, \"  #%-3d\", shr(op2, 8))\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tpushf (o, \"  #%-3d\", op2)\n\t\t\t\t\t\tend\n\t\t\t\t\telseif op2 < 0 then\n\t\t\t\t\t\tpushf (o, \"  \"..formatk(tr, op2))\n\t\t\t\t\telse\n\t\t\t\t\t\tpushf (o, \"  %04d\", op2)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t\tpushf (o, \"\\n\")\n\t\tend\n\tend\n\tif snap then\n\t\tif dumpreg then\n\t\t\tpushf (o, \"....              SNAP   #%-3d  [ \", snapno)\n\t\telse\n\t\t\tpushf (o, \"....        SNAP   #%-3d  [ \", snapno)\n\t\tend\n\t\tpushf (o, printsnap(tr, snap))\n\tend\n\treturn table.concat(o)\nend\n\n\nlocal function get_bytecode(bc)\n\treturn vmdef.bcnames:sub(bc*6+1, bc*6+6):gsub(' ', '')\nend\n\n-- Format trace error message.\nlocal function fmterr(err, info)\n\tif type(err) == \"number\" then\n\t\tif type(info) == \"function\" then info = fmtfunc(info) end\n\t\terr = vmdef.traceerr[err]:format(info)\n\t\tif type(info) == 'number' and err:find('bytecode') then\n\t\t\terr = (\"%s (%s)\"):format(err, get_bytecode(info))\n\t\tend\n\tend\n\treturn err\nend\n\n\nlocal function tracelabel(tr, func, pc, otr, oex)\n\tlocal startex = otr and \"(\"..otr..\"/\"..oex..\") \" or \"\"\n\tlocal info = jutil.traceinfo(tr)\n\tif not info then return '-- no trace info --' end\n\n\tlocal link, ltype = info.link, info.linktype\n\tif ltype == \"interpreter\" then\n\t\treturn (\"%s -- fallback to interpreter\\n\")\n\t\t\t:format(startex)\n\telseif ltype == \"stitch\" then\n\t\treturn (\"%s %s [%s]\\n\")\n\t\t\t:format(startex, ltype, fmtfunc(func, pc))\n\telseif link == tr or link == 0 then\n\t\treturn (\"%s %s\\n\")\n\t\t\t:format(startex, ltype)\n\telseif ltype == \"root\" then\n\t\treturn (\"%s -> %d\\n\")\n\t\t\t:format(startex, link)\n\telse\n\t\treturn (\"%s -> %d %s\\n\")\n\t\t\t:format(startex, link, ltype)\n\tend\nend\n\n----\n\nlocal loomstart, loomstop\ndo\n\tlocal collecting = {[0]=0}\n\tlocal function append(v)\n\t\tlocal c = collecting\n\t\tc[0] = c[0] + 1\n\t\tc[c[0]] = v\n\t\treturn c[0]\n\tend\n\n\tlocal function collect_trace(what, tr, func, pc, otr, oex)\n\t\tappend({'trace', what, tr, func, pc, otr, oex, ''})\n\tend\n\n\tlocal function collect_record(tr, func, pc, depth)\n\t\tappend({'record', tr, func, pc, depth, ''})\n\tend\n\n\tlocal function collect_texit(tr, ex, ngpr, nfpr, ...)\n\t\tappend({'texit', tr, ex, ngpr, nfpr, ...})\n\tend\n\n\tlocal function do_attachs()\n\t\tjit.attach(collect_trace, 'trace')\n\t\tjit.attach(collect_record, 'record')\n\t\tjit.attach(collect_texit, 'texit')\n\tend\n\n\tlocal function do_detachs()\n\t\tjit.attach(collect_texit)\n\t\tjit.attach(collect_record)\n\t\tjit.attach(collect_trace)\n\tend\n\n\tlocal traces_data, seen_funcs = {}, {}\n\tlocal prevtraces = {}\n\tlocal prevexp_t = {\n\t\ttrace = function (what, tr, func, pc, otr, oex)\t\t-- luacheck: ignore func pc\n\t\t\tif what == 'start' then\n\t\t\t\tlocal mcode, addr, loop = jutil.tracemc(tr)\t-- luacheck: ignore mcode loop\n\t\t\t\tif addr ~= nil then\n\t\t\t\t\tif otr and oex then\n\t\t\t\t\t\tsymtab[addr] = (\"Trace #%d (exit %d/%d)\"):format(tr, otr, oex)\n\t\t\t\t\telse\n\t\t\t\t\t\tsymtab[addr] = (\"Trace #%d\"):format(tr)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend,\n\t\trecord = function() end,\n\t\ttexit = function () end,\n\t}\n\tlocal function gettrace(tn)\n\t\tlocal tr = traces_data[tn]\n\t\tif tr then return tr end\n\n\t\ttr = prevtraces[tn] or {\n\t\t\tinfo = jutil.traceinfo(tn) or {},\n\t\t\tir = dump_ir(tn),\n\t\t\tsnap = dump_snap(tn),\n\t\t\tevt = {},\n\t\t\trec = {},\n\t\t\tn = {\n\t\t\t\ttrace = 0,\n\t\t\t\tstart = 0,\n\t\t\t\tstop = 0,\n\t\t\t\tabort = 0,\n\t\t\t\tflush = 0,\n\t\t\t\trecord = 0,\n\t\t\t\ttexit = 0,\n\t\t\t},\n\t\t\texits = {},\n\t\t}\n\t\ttr.mcode = dump_mcode(tn)\n\t\ttraces_data[tn] = tr\n\t\treturn tr\n\tend\n\n\tlocal exp_trace_t = {\n\t\tstart = function (tr, func, pc, otr, oex)\t-- luacheck: ignore func pc\n\t\t\tlocal t = gettrace(tr)\n\t\t\tt.parent = t.parent or otr\n\t\t\tt.p_exit = t.p_exit or oex\n\t\tend,\n\n\t\tstop = function (tr, func, pc, otr, oex)\t-- luacheck: ignore tr func pc otr oex\n\t\tend,\n\n\t\tabort = function (tr, func, pc, otr, oex)\t-- luacheck: ignore func pc\n\t\t\tlocal t = gettrace(tr)\n\t\t\tt.err = t.err or fmterr(otr, oex)\n\t\tend,\n\n\t\tflush = function (tr, func, pc, otr, oex)\t-- luacheck: ignore tr func pc otr oex\n\t\t\tsymtab, nexitsym = {}, 0\n\t\tend,\n\t}\n\tlocal expand_t = {\n\t\ttrace = function (what, tr, func, pc, otr, oex)\n\t\t\tseen_funcs[func] = true\n\t\t\tlocal t = gettrace(tr)\n\t\t\tt.n.trace = t.n.trace + 1\n\t\t\tt.n[what] = (t.n[what] or 0) + 1\n\t\t\tt.tracelabel = t.tracelabel or tracelabel(tr, func, pc, otr, oex)\n\t\t\tt.otr, t.oex = otr, oex\n\n\t\t\tdo\n\t\t\t\tmsg = what=='abort' and fmterr(otr, oex) or nil\n\t\t\t\tt.evt[#t.evt +1] = {\n\t\t\t\t\twhat, func, pc,\n\t\t\t\t\tmsg,\n\t\t\t\t}\n\t\t\t\tif msg then\n\t\t\t\t\tt.rec[#t.rec+1] = {func, pc, (\"%s: %q\"):format(what, msg)}\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal expf = exp_trace_t[what]\n\t\t\treturn expf and expf(tr, func, pc, otr, oex)\n\t\tend,\n\n\t\trecord = function (tr, func, pc, depth)\n\t\t\tlocal t = gettrace(tr)\n\t\t\tt.n.record = t.n.record + 1\n\t\t\tseen_funcs[func] = true\n\t\t\tt.rec[#t.rec+1] = {func, pc, bcline(func, pc, (' .'):rep(depth))}\n\t\t\tif pc >= 0 and band(jutil.funcbc(func, pc), 0xff) < 16 then\n\t\t\t\tt.rec[#t.rec+1] = {func, pc+1, bcline(func, pc+1, (' .'):rep(depth))}\n\t\t\tend\n\t\tend,\n\n\t\ttexit = function (tr, ex, ngpr, nfpr, ...)\n\t\t\tlocal t = gettrace(tr)\n\t\t\tt.n.texit = t.n.texit + 1\n\t\t\tt.exits[ex] = (t.exits[ex] or 0) + 1\n\t\t\tt.evt[#t.evt+1] = {'exit', ex, ngpr, nfpr, ...}\n\t\tend,\n\t}\n\n\tfunction loomstart(clear)\n\t\tif clear then\n\t\t\tfor k, v in pairs(traces_data) do\n\t\t\t\tprevtraces[k] = v\n\t\t\tend\n\t\t\ttraces_data, seen_funcs = {}, {}\n\t\t\tcollecting = {[0]=0}\n\t\tend\n\t\tdo_attachs()\n\tend\n\n\tfunction loomstop(f, ...)\n\t\tdo_detachs()\n\t\tfor _, v in ipairs(collecting) do\n\t\t\tprevexp_t[v[1]](unpack(v, 2, table.maxn(v)))\n\t\tend\n\t\tfor _, v in ipairs(collecting) do\n\t\t\texpand_t[v[1]](unpack(v, 2, table.maxn(v)))\n\t\tend\n\n\t\tfor tn, tr in pairs(traces_data) do\n\t\t\tgettrace(tr.otr or tn)\n\t\t\tgettrace(tr.info.link or tn)\n\t\t\tif tr.mcode then\n\t\t\t\tfor tns in tr.mcode:gmatch('Trace #(%d+)') do\n\t\t\t\t\tgettrace(tonumber(tns))\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tfor _, tr in pairs(traces_data) do\n\t\t\tfor _, rec in ipairs(tr.rec) do\n\t\t\t\tseen_funcs[rec[1]] = true\n\t\t\tend\n\t\t\tfor _, evt in ipairs(tr.evt) do\n\t\t\t\tif type(evt[2]) == 'function' then\n\t\t\t\t\tseen_funcs[evt[2]] = true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tlocal funcslist = {}\n\t\tfor fun in pairs(seen_funcs) do\n\t\t\tfor subf, fi in pairs(func_bc(fun)) do\n\t\t\t\tfuncslist[subf] = fi\n\t\t\tend\n\t\tend\n\n\t\tif f then\n\t\t\treturn f(traces_data, funcslist, ...)\n\t\tend\n\t\treturn traces_data, funcslist\n\tend\nend\n--------------------------------------\nlocal function srclines(fn)\n\tlocal t, f = {}, io.open(fn)\n\tif f then\n\t\tfor l in f:lines() do\n\t\t\tt[#t+1] = l\n\t\tend\n\t\tf:close()\n\tend\n\treturn t\nend\n\nlocal function defget(t, k, d)\n\tlocal v = t[k] or d\n\tif v == nil then v = {} end\n\tif t[k] == nil then t[k] = v end\n\treturn v\nend\n\n\nlocal function annotated(funcs, traces)\n\tlocal ranges = {}\n\tfor f, fi in pairs(funcs) do\n\t\tif type(f)=='function' and fi.source then\n\t\t\tlocal srcranges = defget(ranges, fi.source:gsub('^@', ''), nil)\n\t\t\tlocal lineranges = defget(srcranges, fi.linedefined, nil)\n\t\t\tlineranges[f] = fi\n\t\tend\n\tend\n\n\tlocal presources = {}\n\tdo\n\t\tlocal cmdlinesrc = ''\n\t\tfor k, v in pairs(arg) do\n\t\t\tif type(k) == 'number' and v == '-e' then\n\t\t\t\tcmdlinesrc = cmdlinesrc .. arg[k+1]\n\t\t\tend\n\t\tend\n\t\tlocal cmdlines = {}\n\t\tfor l in (cmdlinesrc..'\\r'):gmatch('(.-)[\\r\\n]') do\n\t\t\tcmdlines[#cmdlines+1] = l\n\t\tend\n\t\tpresources['=(command line)'] = cmdlines\n\tend\n\n\tlocal o = {}\n\tfor srcname, srcranges in sortedpairs(ranges) do\n\t\to[srcname] = {}\n\t\tlocal of, src = o[srcname], presources[srcname] or srclines(srcname)\n\t\tlocal lastline = nil\n\t\tlocal function newline(i, func, pc, bcl)\n\t\t\tlocal nl = {\n\t\t\t\ti = i,\n\t\t\t\tsrc = src[i],\n\t\t\t\tfunc = func,\n\t\t\t\tpc = pc,\n\t\t\t\tbc = bcl or '',\n\t\t\t\tback = type(i)=='number' and i <= lastline,\n\t\t\t\ttr = {},\n\t\t\t\tevt = {},\n\t\t\t}\n\t\t\tof[#of+1] = nl\n\t\t\treturn nl\n\t\tend\n\t\tfor startline, lst in allipairs(srcranges, 0) do\n\t\t\tfor _, fi in pairs(lst) do\n\t\t\t\tlastline = math.max(0, startline-2)\n\t\t\t\tfor pc, l in sortedpairs(fi.bytecode or {}) do\n\t\t\t\t\tlocal lnum, bcl = unpack(l)\n\t\t\t\t\tif lnum > lastline + 5 then\n\t\t\t\t\t\tfor i = lastline+1, lastline+3 do\n\t\t\t\t\t\t\tnewline (i)\n\t\t\t\t\t\tend\n\t\t\t\t\t\tnewline ('...')\n\t\t\t\t\t\tfor i = lnum-2, lnum-1 do\n\t\t\t\t\t\t\tnewline(i, fi.func)\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tfor i = lastline+1, lnum-1 do\n\t\t\t\t\t\t\tnewline(i, fi.func)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\tnewline(lnum, fi.func, pc, bcl)\n\t\t\t\t\tlastline = math.max(lastline, lnum)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tfor i, tr in allipairs(traces) do\n\t\tfor j, rec in ipairs(tr.rec) do\n\t\t\tlocal f, pc, bcl = unpack (rec)\t\t\t-- luacheck: ignore bcl\n\t\t\tfor srcname, osrc in pairs(o) do\t\t-- luacheck: ignore srcname\n\t\t\t\tfor _, ol in ipairs(osrc) do\n\t\t\t\t\tif ol.func == f and ol.pc == pc and #ol.bc>0 then\n\t\t\t\t\t\tol.tr[#ol.tr+1] = {i, j}\n\t\t\t\t\t\trec[#rec+1] = {name=srcname, i=ol.i, l=ol.src}\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tfor _, evt in ipairs(tr.evt) do\n\t\t\tlocal what, func, pc, msg = unpack(evt)\n\t\t\tfor srcname, osrc in pairs(o) do\t\t-- luacheck: ignore srcname\n\t\t\t\tfor _, ol in ipairs(osrc) do\n\t\t\t\t\tif ol.func == func and ol.pc == pc and #ol.bc>0 then\n\t\t\t\t\t\tlocal k = what .. (msg and ': '..msg or '')\n\t\t\t\t\t\tol.evt[k] = (ol.evt[k] or 0) + 1\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\treturn o\nend\n\n--------------------------------------\n\n--[[\n\ttemplate compiling function, loosely derived from:\n\t\thttps://github.com/dannote/lua-template/blob/master/template.lua\n\tby Danila Poyarkov\n--]]\nlocal template\ndo\n\tlocal _esc = {\n\t\t['&'] = '&amp;',\n\t\t['<'] = '&lt;',\n\t\t['>'] = '&gt;',\n\t\t['\"'] = '&quot;',\n\t}\n\tlocal function escape(s)\n\treturn tostring(s or ''):gsub('[><&\"]', _esc)\n\tend\n\n\tfunction template (tpl)\n\t\tlocal tplname = 'tmpl'\n\t\tif not tpl:find('\\n', 1, true) then\n\t\t\ttplname = tpl\n\t\t\tlocal f = assert(io.open(tpl))\n\t\t\ttpl = assert(f:read('*a'))\n\t\t\tf:close()\n\t\tend\n\n\t\tlocal args = {'_e'}\n\t\ttpl = tpl:gsub('{@(.-)}', function (argl)\n\t\t\targl:gsub('([_%a][_%w]*)', function (a) args[#args+1] = a return '' end)\n\t\t\treturn ''\n\t\tend)\n\n\t\tlocal src = (\n\t\t\t'local %s = ... ' ..\n\t\t\t'local _o = {} ' ..\n\t\t\t'local function _p(x) _o[#_o+1] = tostring(x or \"\") end ' ..\n\t\t\t'local function _fp(f, ...) _p(f:format(...)) end '..\n\t\t\t'local function _ep(x) _p(_e(x)) end ' ..\n\t\t\t'_p[=[%s]=] ' ..\n\t\t\t'return table.concat(_o)')\n\t\t:format(\n\t\t\ttable.concat(args, ', '),\n\t\t\ttpl\n\t\t\t\t:gsub('[][]=[][]', ']=] _p\"%1\" _p[=[')\n\t\t\t\t:gsub('{{=', ']=] _p(')\n\t\t\t\t:gsub('{{:', ']=] _fp(')\n\t\t\t\t:gsub('{{', ']=] _ep(')\n\t\t\t\t:gsub('}}', ') _p[=[')\n\t\t\t\t:gsub('{%%', ']=] ')\n\t\t\t\t:gsub('%%}', ' _p[=[')\n\t\t)\n\t\tlocal f = assert(loadstring(src, tplname))\n\t\treturn function (...)\n\t\t\treturn f(escape, ...)\n\t\tend\n\tend\nend\n\n\n-------------------------------------\nlocal defer\n\nreturn {\n\ton = loomstart,\n\toff = loomstop,\n\n\tstart = function (opt, out)\n\t\tlocal tmpl = template(opt or 'loom.html')\n\t\tdefer = newproxy(true)\n\t\tgetmetatable(defer).__gc = function() xpcall(function ()\n\t\t\tlocal o = loomstop(tmpl)\n\t\t\tout = type(out)=='string' and assert(io.open(out, 'w'))\n\t\t\t\t\tor out or io.stdout\n\t\t\tout:write(o)\n\t\tend, function(err) print(debug.traceback(err)) end) end\n\n\t\tloomstart()\n\tend,\n\n\ttemplate = template,\n\tannotated = annotated,\n\tallipairs = allipairs,\n\tsortedpairs = sortedpairs,\n}\n"
  },
  {
    "path": "loom.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\t{@ traces, funcs}\n\t{%\n\t\tlocal loom = require 'jit.loom'\n\t\tlocal function class(t)\n\t\t\tif not t then return '' end\n\t\t\to = {}\n\t\t\tfor k, v in pairs(t) do\n\t\t\t\tif v then o[#o+1] = k end\n\t\t\tend\n\t\t\tif #o == 0 then return '' end\n\t\t\treturn 'class=\"'..table.concat(o, ' ')..'\"'\n\t\tend\n\n\t\tlocal _ft_, _fndx_ = {}, 0\n\t\tlocal function funclabel(f)\n\t\t\tif not f then return '' end\n\t\t\tif _ft_[f] == nil then\n\t\t\t\t_fndx_ = _fndx_+1\n\t\t\t\t_ft_[f] = ('fn%03d'):format(_fndx_)\n\t\t\tend\n\t\t\treturn _ft_[f]\n\t\tend\n\n\t\tlocal function lines(s)\n\t\t\ts = s or ''\n\t\t\tlocal o = {}\n\t\t\tfor l in s:gmatch('[^\\r\\n]+') do\n\t\t\t\to[#o+1] = l\n\t\t\tend\n\t\t\treturn o\n\t\tend\n\n\t\tlocal function cols(s, cwl)\n\t\t\tlocal o, start = {}, 1\n\t\t\tfor i, w in ipairs(cwl) do\n\t\t\t\to[i] = s:sub(start, start+w-1):gsub('%s+$', '')\n\t\t\t\tstart = start+w\n\t\t\tend\n\t\t\treturn o\n\t\tend\n\n\t\tlocal function is_irref(f)\n\t\t\tif f:match('^%d%d%d%d$') then\n\t\t\t\treturn 'ref_'..f\n\t\t\tend\n\t\tend\n\n\t\tlocal function all_refs(s)\n\t\t\tc = {}\n\t\t\tfor ref in s:gmatch('%d+') do\n\t\t\t\tc[#c+1] = is_irref(ref)\n\t\t\tend\n\t\t\treturn table.concat(c, ' ')\n\t\tend\n\n\t\tlocal function table_ir(txt)\n\t\t\tlocal o = lines(txt)\n\t\t\tlocal cwl = {5, 6, 3, 4, 7, 6, 1000}\n\t\t\tfor i, l in ipairs(o) do\n\t\t\t\tl = cols(l, cwl)\n\t\t\t\tlocal class = {is_irref(l[1])}\n\t\t\t\tif l[5] == 'SNAP' then\n\t\t\t\t\tclass[#class+1] = 'snap_'..l[6]:sub(2)\n\t\t\t\t\tl.title = l[7]\n\t\t\t\t\tl[7] = ('<span class=\"opt\">%s</span>'):format(l[7])\n\t\t\t\tend\n\t\t\t\tl.class = next(class) and table.concat(class, ' ')\n\t\t\t\to[i] = l\n\t\t\tend\n\t\t\treturn o\n\t\tend\n\n\t\tlocal function annot_mcode(txt)\n\t\t\tif type(txt) ~= 'string' then return '' end\n\t\t\ttxt = txt:gsub('%(exit (%d+)/(%d+)%)', function (a, b)\n\t\t\t\ta, b = tonumber(a), tonumber(b)\n\t\t\t\treturn ('(exit %d/%d [n=%d])'):format(a, b, traces[a].exits[b] or 0)\n\t\t\tend)\n\t\t\ttxt = _e(txt)\n\t\t\ttxt = txt:gsub('Trace #(%d+)', function (tr)\n\t\t\t\treturn ('<span class=\"tr%03d\">Trace #%d</span>'):format(\n\t\t\t\t\ttr, tr)\n\t\t\tend)\n\t\t\treturn txt\n\t\tend\n\n\t\tlocal cmdline = ''\n\t\tdo\n\t\t\tlocal minarg, maxarg = 1000,-1000\n\t\t\tfor k in pairs(arg) do\n\t\t\t\tif type(k) == 'number' then\n\t\t\t\t\tminarg = math.min(k, minarg)\n\t\t\t\t\tmaxarg = math.max(k, maxarg)\n\t\t\t\tend\n\t\t\tend\n\t\t\tlocal newarg = {}\n\t\t\tfor i = minarg, maxarg do\n\t\t\t\tlocal v = tostring(arg[i])\n\t\t\t\tif v:find('[^%w.,/=_-]') then\n\t\t\t\t\tv = ('%q'):format(v)\n\t\t\t\tend\n\t\t\t\tnewarg[i] = v\n\t\t\tend\n\t\t\tcmdline = table.concat(newarg, ' ', minarg, maxarg)\n\t\tend\n\n\t\tlocal annotated = loom.annotated(funcs, traces)\n\t%}\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<style media=\"screen\" type=\"text/css\">\n\t\t\t.code {\n\t\t\t\tfont-family: monospace;\n\t\t\t\twhite-space: pre;\n\t\t\t\ttab-size: 4;\n\t\t\t}\n\t\t\t.opt { display: none; }\n\t\t\t.codespan {\n\t\t\t\twidth: 100%;\n\t\t\t}\n\t\t\t.bordertop td {\n\t\t\t\tborder-top: thin lightgray solid;\n\t\t\t}\n\t\t\t.phantom {\n\t\t\t\tcolor: #ccc;\n\t\t\t}\n\t\t\t.white {\n\t\t\t\tbackground-color: white;\n\t\t\t}\n\t\t\t.hilight {\n\t\t\t\tbackground-color: lightsteelblue;\n\t\t\t}\n\t\t\t{% for f, fi in pairs(funcs) do %}.{{funclabel(f)}} {\n\t\t\t\tbackground-color: hsla({{math.random(360)}}, 100%, 90%, 1);\n\t\t\t}\n\t\t\t{% end %}\n\n\t\t\t{% for i = 1, table.maxn(traces) do %}{{:'.tr%03d', i}} {\n\t\t\t\tbackground-color: hsla({{math.random(360)}}, 80%, 75%, 1);\n\t\t\t}\n\t\t\t{% end %}\n\t\t</style>\n\t\t<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n\t\t<script>\n\t\t\tvar sameref = function (elm, parent) {\n\t\t\t\tvar refmatch = elm.className.match(/ref_\\d+/);\n\t\t\t\treturn refmatch ? $(elm).closest(parent).find('.'+refmatch[0])\n\t\t\t\t\t\t: $();\n\t\t\t\treturn $(elm).closest(parent).find('.'+refclass);\n\t\t\t}\n\t\t\t$(function() {\n\t\t\t\t$('th.bc').click(function(e) {\n\t\t\t\t\t$('.bc .opt').toggle();\n\t\t\t\t});\n\t\t\t\t$('th.ir').click(function(e) {\n\t\t\t\t\t$('.ir .opt').toggle();\n\t\t\t\t});\n\t\t\t\t$('th.titlebar').click(function(e) {\n\t\t\t\t\t$(e.target).closest('tr').siblings().toggle();\n\t\t\t\t});\n\t\t\t\t$('[class^=\"ref_\"]').mouseenter(function(e) {\n\t\t\t\t\tsameref(e.target, '.ir').addClass('hilight');\n\t\t\t\t}).mouseleave(function(e){\n\t\t\t\t\tsameref(e.target, '.ir').removeClass('hilight');\n\t\t\t\t});\n\t\t\t});\n\t\t</script>\n\t\t<title>{{cmdline}}</title>\n\t</head>\n\t<body>\n\t<h2>{{=cmdline:gsub('\\\\[\\r\\n]+',\"<br/>\")}}</h2>\n\t<table class=\"code\" cellpadding=\"0\" cellspacing=\"0\">\n\t\t{% for filename, filedata in pairs(annotated) do\n\t\t\tlocal lastline\n\t\t\t%}\n\t\t\t<tr>\n\t\t\t\t<th colspan=\"2\">{{ filename }}</th>\n\t\t\t\t<th colspan=\"3\">Bytecode</th>\n\t\t\t</th>\n\t\t\t{% for i, l in loom.sortedpairs(filedata) do\n\t\t\t\tlocal notsame = l.i ~= lastline\n\t\t\t\tlastline = l.i\n\t\t\t\t%}\n\t\t\t<tr {{= class{bordertop=notsame and l.bc ~= ''} }}>\n\t\t\t\t<td {{= class{phantom=l.back} }}> {{ notsame and l.i or '' }} </td>\n\t\t\t\t<td {{= class{phantom=l.back} }}> {{ notsame and l.src or '' }} </td>\n\t\t\t\t<td {{= class{[funclabel(l.func)] = l.bc ~= ''} }}> {{ l.bc }} </td>\n\t\t\t\t<td>{% for i, tr in ipairs(l.tr or {}) do\n\t\t\t\t\tlocal trref = ('tr%03d'):format(tr[1])\n\t\t\t\t\tlocal lnref = ('tr%03d_%03d'):format(tr[1], tr[2])\n\t\t\t\t\t%} <a href=\"#{{trref}}\" name=\"{{lnref}}\"><span\n\t\t\t\t\t\tid=\"{{lnref}}\"\n\t\t\t\t\t\tclass=\"{{trref}}\"\n\t\t\t\t\t>{{tr[1]}}/{{tr[2]}}</span></a> {%\n\t\t\t\tend %}</td>\n\t\t\t\t<td>{% for msg, n in pairs(l.evt or {}) do\n\t\t\t\t\t%} <span>\"{{msg}}\" [n={{n}}]</span> {%\n\t\t\t\tend %}</td>\n\t\t\t</tr>\n\t\t\t{% end %}\n\t\t{% end %}\n\t</table>\n\n\t{% for i, tr in loom.allipairs(traces) do local prevsrc%}\n\t\t<br/>\n\t\t<a name=\"#{{:'tr%0dd', i}}\"><table class=\"popup trace {{:'tr%03d', i}}\" id=\"{{:'tr%03d', i}}\" cellpadding=\"4\">\n\t\t\t<tr>\n\t\t\t\t<th colspan=\"3\" class=\"titlebar\">Trace #{{i}}: {{tr.tracelabel}}</th>\n\t\t\t</tr>\n\t\t\t<tr class=\"white\">\n\t\t\t\t<th class=\"bc\" >bytecode</th>\n\t\t\t\t<th class=\"ir\" >IR</th>\n\t\t\t\t<th class=\"mcode\" >mcode</th>\n\t\t\t</tr>\n\t\t\t<tr class=\"white\" valign=\"top\">\n\t\t\t\t<td class=\"code bc\"><table cellpadding=\"0\" cellspacing=\"0\">\n\t\t\t\t\t{% for j, rec in ipairs(tr.rec) do\n\t\t\t\t\t\tlocal f, pc, l, src = unpack(rec)\n\t\t\t\t\t\tlocal srcline = src and ('%s:%d %s'):format(src.name, src.i, src.l or '')\n\t\t\t\t\t\tlocal lnref = ('tr%03d_%03d'):format(i, j)\n\t\t\t\t\t\t%}\n\t\t\t\t\t<tr class=\"code\">\n\t\t\t\t\t\t<td class=\"{{:'tr%03d', i}}\"><a href=\"#{{lnref}}\">{{i}}/{{j}}</a> </td>\n\t\t\t\t\t\t<td class=\"{{funclabel(f)}}\"> {{l}} </td>\n\t\t\t\t\t\t<td class=\"src opt\">{{srcline ~= prevsrc and srcline or ''}}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t{% prevsrc = srcline\n\t\t\t\t\tend %}\n\t\t\t\t</table></td>\n\t\t\t\t<td class=\"code ir\"><table>\n\t\t\t\t\t{%for _, l in ipairs(table_ir(tr.ir)) do %}\n\t\t\t\t\t\t<tr class=\"{{=l.class or ''}}\" title=\"{{=l.title}}\">{% for _, f in ipairs(l) do %}\n\t\t\t\t\t\t\t<td class=\"{{=all_refs(f)}}\">{{=f}}</td>\n\t\t\t\t\t\t{% end %}</tr>\n\t\t\t\t\t{% end %}\n\t\t\t\t</table></td>\n\t\t\t\t<td class=\"code mcode\">{{=annot_mcode(tr.mcode)}}</td>\n\t\t\t</tr>\n\t\t</table></a>\n\t{% end %}\n\t</body>\n</html>\n"
  },
  {
    "path": "sample.lua",
    "content": "local Sm = {}\n\nfunction Sm.lulu()\n\tfor i = 1, 1000 do\n\t\tfor j = 1, 1000 do\n\t\tend\n\tend\nend\n\nfunction Sm.motivating_example_1()\n\tlocal x, z = 0, nil\n\tfor i=1,100 do\n\t\tlocal t = {i}\n\t\tif i == 90 then\n\t\t\tz = t\n\t\tend\n\t\tx = x + t[1]\n\tend\n\tprint(x, z[1])\nend\n\nfunction Sm.motivating_example_2()\n\tlocal x, z = 0, nil\n\tfor i=1,100 do\n\t\tif i == 90 then\n\t\t\tlocal t = {i}\n\t\t\tz = t\n\t\tend\n\t\tx = x + i\n\tend\n\tprint(x, z[1])\nend\n\nfunction Sm.resinking()\n\tlocal z = nil\n\tfor i=1,200 do\n\t\tlocal t = {i}\n\t\tif i > 100 then\n\t\t\tif i == 190 then z = t end\n\t\tend\n\tend\n\tprint(z[1])\nend\n\nfunction Sm.pointadds()\n\tlocal point\n\tpoint = {\n\t\tnew = function(self, x, y)\n\t\t\treturn setmetatable({x=x, y=y}, self)\n\t\tend,\n\t\t__add = function(a, b)\n\t\t\treturn point:new(a.x + b.x, a.y + b.y)\n\t\tend,\n\t}\n\tpoint.__index = point\n\tlocal a, b = point:new(1.5, 2.5), point:new(3.25, 4.75)\n\tfor i=1,100000000 do\n\t\ta = (a + b) + b\n\tend\n\tprint(a.x, a.y)\nend\n\nfunction Sm.tdup(x)\n\treturn { foo=1, bar=2, 1,2,x,4 }\nend\n\nfunction Sm.miltdup(x)\n\tfor i=1,1000 do Sm.tdup(i) end\nend\n\nfunction Sm.call_some()\n\tprint (\"one\")\n\tSm.motivating_example_1()\n\tprint (\"end\")\nend\n\n\nreturn Sm\n"
  }
]