feat(bytecode): add disassembler

This commit is contained in:
dualfroz committed 2026-08-24 19:15:00 +02:00
1 parent d72611f946
commit 31968bec16
1 file changed
+91
@@ -0,0 +1,91 @@
package lumen.bytecode
import lumen.runtime.FunctionValue
import lumen.runtime.Value
import lumen.runtime.displayString
object Disassembler {
fun dump(function: FunctionValue, out: Appendable = System.out) {
out.append("== ${function.name} (arity ${function.arity}, upvalues ${function.upvalueCount}) ==\n")
val chunk = function.chunk
var offset = 0
while (offset < chunk.code.size) {
offset = instruction(chunk, offset, out)
}
for (constant in chunk.constants) {
if (constant is FunctionValue) dump(constant, out)
}
}
private fun instruction(chunk: Chunk, offset: Int, out: Appendable): Int {
out.append(pad(offset))
out.append(" ")
val op = Opcode.from(chunk.code[offset])
return when (op) {
Opcode.Constant, Opcode.GetGlobal, Opcode.SetGlobal, Opcode.DefineGlobal ->
constantInstruction(op, chunk, offset, out)
Opcode.GetLocal, Opcode.SetLocal, Opcode.GetUpvalue, Opcode.SetUpvalue,
Opcode.Call, Opcode.BuildList, Opcode.BuildMap ->
operandInstruction(op, chunk, offset, out)
Opcode.Jump, Opcode.JumpIfFalse, Opcode.Loop ->
jumpInstruction(op, chunk, offset, out)
Opcode.ForIter -> forIterInstruction(chunk, offset, out)
Opcode.Invoke -> invokeInstruction(chunk, offset, out)
Opcode.Closure -> closureInstruction(chunk, offset, out)
else -> {
out.append(op.name).append("\n")
offset + 1
}
}
}
private fun constantInstruction(op: Opcode, chunk: Chunk, offset: Int, out: Appendable): Int {
val index = chunk.code[offset + 1]
out.append(op.name).append(" ").append(index.toString())
.append(" (").append(constantText(chunk.constants[index])).append(")\n")
return offset + 2
}
private fun operandInstruction(op: Opcode, chunk: Chunk, offset: Int, out: Appendable): Int {
out.append(op.name).append(" ").append(chunk.code[offset + 1].toString()).append("\n")
return offset + 2
}
private fun jumpInstruction(op: Opcode, chunk: Chunk, offset: Int, out: Appendable): Int {
val jump = chunk.code[offset + 1]
val target = if (op == Opcode.Loop) offset + 2 - jump else offset + 2 + jump
out.append(op.name).append(" ").append(jump.toString())
.append(" -> ").append(target.toString()).append("\n")
return offset + 2
}
private fun forIterInstruction(chunk: Chunk, offset: Int, out: Appendable): Int {
val slot = chunk.code[offset + 1]
val jump = chunk.code[offset + 2]
val target = offset + 3 + jump
out.append("ForIter slot ").append(slot.toString())
.append(" exit -> ").append(target.toString()).append("\n")
return offset + 3
}
private fun invokeInstruction(chunk: Chunk, offset: Int, out: Appendable): Int {
val nameIndex = chunk.code[offset + 1]
val argCount = chunk.code[offset + 2]
out.append("Invoke ").append(constantText(chunk.constants[nameIndex]))
.append(" (").append(argCount.toString()).append(" args)\n")
return offset + 3
}
private fun closureInstruction(chunk: Chunk, offset: Int, out: Appendable): Int {
val index = chunk.code[offset + 1]
val function = chunk.constants[index] as FunctionValue
out.append("Closure ").append(index.toString())
.append(" (").append(function.name).append(")\n")
return offset + 2 + function.upvalueCount * 2
}
private fun constantText(value: Value): String =
if (value is FunctionValue) "<fn ${value.name}>" else displayString(value)
private fun pad(offset: Int): String = offset.toString().padStart(4, '0')
}