// disasm.ce — source-interleaved disassembly // // Usage: // cell disasm Disassemble all functions (mcode) // cell disasm --optimized Disassemble optimized IR (streamline) // cell disasm --fn Show only function N or named function // cell disasm --line Show instructions from source line N var fd = use("fd") var shop = use("internal/shop") var pad_right = function(s, w) { var r = s while (length(r) < w) { r = r + " " } return r } var fmt_val = function(v) { if (is_null(v)) return "null" if (is_number(v)) return text(v) if (is_text(v)) return `"${v}"` if (is_object(v)) return text(v) if (is_logical(v)) return v ? "true" : "false" return text(v) } var run = function() { var use_optimized = false var fn_filter = null var line_filter = null var filename = null var i = 0 var compiled = null var source_text = null var source_lines = null var main_name = null var fi = 0 var func = null var fname = null while (i < length(args)) { if (args[i] == '--optimized') { use_optimized = true } else if (args[i] == '--fn') { i = i + 1 fn_filter = args[i] } else if (args[i] == '--line') { i = i + 1 line_filter = number(args[i]) } else if (args[i] == '--help' || args[i] == '-h') { log.console("Usage: cell disasm [--optimized] [--fn ] [--line ] ") log.console("") log.console(" --optimized Use optimized IR (streamline) instead of raw mcode") log.console(" --fn Filter to function by index or name") log.console(" --line Show only instructions from source line N") return null } else if (!starts_with(args[i], '-')) { filename = args[i] } i = i + 1 } if (!filename) { log.console("Usage: cell disasm [--optimized] [--fn ] [--line ] ") return null } // Compile if (use_optimized) { compiled = shop.compile_file(filename) } else { compiled = shop.mcode_file(filename) } // Read source file source_text = text(fd.slurp(filename)) source_lines = array(source_text, "\n") // Helpers var get_source_line = function(line_num) { if (line_num < 1 || line_num > length(source_lines)) return null return source_lines[line_num - 1] } var first_instr_line = function(func) { var instrs = func.instructions var i = 0 var n = 0 if (instrs == null) return null while (i < length(instrs)) { if (is_array(instrs[i])) { n = length(instrs[i]) return instrs[i][n - 2] } i = i + 1 } return null } var func_has_line = function(func, target) { var instrs = func.instructions var i = 0 var n = 0 if (instrs == null) return false while (i < length(instrs)) { if (is_array(instrs[i])) { n = length(instrs[i]) if (instrs[i][n - 2] == target) return true } i = i + 1 } return false } var fn_matches = function(index, name) { var match = null if (fn_filter == null) return true if (index >= 0 && fn_filter == text(index)) return true if (name != null) { match = search(name, fn_filter) if (match != null && match >= 0) return true } return false } var func_name_by_index = function(fi) { var f = null if (compiled.functions == null) return null if (fi < 0 || fi >= length(compiled.functions)) return null f = compiled.functions[fi] return f.name } var dump_function = function(func, name, index) { var nr_args = func.nr_args != null ? func.nr_args : 0 var nr_slots = func.nr_slots != null ? func.nr_slots : 0 var nr_close = func.nr_close_slots != null ? func.nr_close_slots : 0 var instrs = func.instructions var start_line = first_instr_line(func) var header = null var i = 0 var pc = 0 var instr = null var op = null var n = 0 var parts = null var j = 0 var operands = null var instr_line = null var last_line = null var src = null var line_str = null var instr_text = null var target_name = null header = `\n=== ${name} (args=${text(nr_args)}, slots=${text(nr_slots)}, closures=${text(nr_close)})` if (start_line != null) { header = header + ` [line ${text(start_line)}]` } header = header + " ===" log.compile(header) if (instrs == null || length(instrs) == 0) { log.compile(" (empty)") return null } while (i < length(instrs)) { instr = instrs[i] if (is_text(instr)) { if (!starts_with(instr, "_nop_") && line_filter == null) { log.compile(` ${instr}:`) } } else if (is_array(instr)) { op = instr[0] n = length(instr) instr_line = instr[n - 2] if (line_filter != null && instr_line != line_filter) { pc = pc + 1 i = i + 1 continue } if (instr_line != last_line && instr_line != null) { src = get_source_line(instr_line) if (src != null) { src = trim(src) } if (last_line != null) { log.compile("") } if (src != null && length(src) > 0) { log.compile(` --- line ${text(instr_line)}: ${src} ---`) } else { log.compile(` --- line ${text(instr_line)} ---`) } last_line = instr_line } parts = [] j = 1 while (j < n - 2) { parts[] = fmt_val(instr[j]) j = j + 1 } operands = text(parts, ", ") line_str = instr_line != null ? `:${text(instr_line)}` : "" instr_text = ` ${pad_right(text(pc), 6)}${pad_right(op, 15)}${operands}` // Cross-reference for function creation instructions target_name = null if (op == "function" && n >= 5) { target_name = func_name_by_index(instr[2]) } if (target_name != null) { instr_text = pad_right(instr_text, 65) + line_str + ` ; -> [${text(instr[2])}] ${target_name}` } else { instr_text = pad_right(instr_text, 65) + line_str } log.compile(instr_text) pc = pc + 1 } i = i + 1 } return null } // Process functions main_name = compiled.name != null ? compiled.name : "
" if (compiled.main != null) { if (fn_matches(-1, main_name)) { if (line_filter == null || func_has_line(compiled.main, line_filter)) { dump_function(compiled.main, main_name, -1) } } } if (compiled.functions != null) { fi = 0 while (fi < length(compiled.functions)) { func = compiled.functions[fi] fname = func.name != null ? func.name : "" if (fn_matches(fi, fname)) { if (line_filter == null || func_has_line(func, line_filter)) { dump_function(func, `[${text(fi)}] ${fname}`, fi) } } fi = fi + 1 } } return null } run() $stop()