Files
aego/script/bytecode/disasm.go
T

197 lines
5.2 KiB
Go

package bytecode
import (
"fmt"
"io"
"strconv"
"strings"
)
func Disassemble(w io.Writer, m *Module) {
DisassembleSource(w, m, nil)
}
func short(name string) string {
return name[strings.LastIndexByte(name, '.')+1:]
}
func DisassembleSource(w io.Writer, m *Module, source func(file string, line int) string) {
var b strings.Builder
fmt.Fprintf(&b, "module %s bytecode=%d lang=%d\n", m.Package, m.Version, m.LangVersion)
for i, imp := range m.Imports {
fmt.Fprintf(&b, "import h%d %s.%s %s\n", i, imp.Module, imp.Name, imp.Sig)
}
for i, e := range m.Externs {
fmt.Fprintf(&b, "extern x%d %s.%s params=%d results=%d\n", i, e.Package, e.Name, e.Params, e.Results)
}
for i, t := range m.Types {
fmt.Fprintf(&b, "type t%d []%s width=%d mask=%#x\n", i, t.Elem, t.Width, t.Mask)
}
for _, e := range m.Enums {
fmt.Fprintf(&b, "enum %s %s\n", e.Name, strings.Join(e.Members, " "))
}
for i, r := range m.ScriptRefs {
fmt.Fprintf(&b, "scriptref s%d %s\n", i, r)
}
for i, x := range m.XFields {
fmt.Fprintf(&b, "xfield f%d s%d.%s %s slots=%d\n", i, x.Ref, x.Name, x.Type, x.Slots)
}
for i, x := range m.XMethods {
fmt.Fprintf(&b, "xmethod m%d s%d.%s params=%d results=%d\n", i, x.Ref, x.Name, x.Params, x.Results)
}
for i, c := range m.Connects {
fmt.Fprintf(&b, "connect c%d %s.%s -> %s.%s\n", i, c.Source, c.Signal, c.Target, c.Handler)
}
for i := range m.Scripts {
s := &m.Scripts[i]
fmt.Fprintf(&b, "script %s slots=%d ready=%d fixed=%d update=%d reloaded=%d\n", s.FullName, s.Slots, s.Ready, s.FixedUpdate, s.Update, s.Reloaded)
for _, sg := range s.Signals {
params := make([]string, len(sg.Params))
for j, p := range sg.Params {
params[j] = p.String()
}
fmt.Fprintf(&b, " signal %s(%s) slots=%d\n", sg.Name, strings.Join(params, ", "), sg.Slots)
}
for _, h := range s.Events {
fmt.Fprintf(&b, " event %s f%d\n", h.Name, h.Func)
}
for _, f := range s.Fields {
exp := ""
if f.Export {
exp = " export"
}
if f.Persist {
exp += " persist"
}
fmt.Fprintf(&b, " field %s %s slot=%d+%d%s\n", f.Name, f.Type, f.Slot, f.Slots, exp)
}
}
for i := range m.Functions {
disasmFunc(&b, m, i, source)
}
io.WriteString(w, b.String())
}
func disasmFunc(b *strings.Builder, m *Module, fi int, source func(string, int) string) {
f := &m.Functions[fi]
fmt.Fprintf(b, "\nfunc f%d %s params=%d results=%d regs=%d", fi, f.Name, f.Params, f.Results, f.Registers)
if f.Script >= 0 {
fmt.Fprintf(b, " script=%d", f.Script)
}
if f.Coroutine {
b.WriteString(" coroutine")
}
b.WriteByte('\n')
last := -1
for pc, in := range f.Code {
if line := f.Line(pc); line > 0 && line != last {
last = line
text := ""
if source != nil {
text = strings.TrimSpace(source(f.File, line))
}
fmt.Fprintf(b, " ; %s:%d %s\n", f.File, line, text)
}
fmt.Fprintf(b, " %04d %-8s %s\n", pc, in.Op(), operands(m, f, pc, in))
}
}
func operands(m *Module, f *Function, pc int, in Instr) string {
info := in.Op().Info()
var parts []string
add := func(k Operand, v int) {
if s := operand(m, f, pc, k, v); s != "" {
parts = append(parts, s)
}
}
add(info.A, int(in.A()))
switch info.Format {
case FormatABC:
add(info.B, int(in.B()))
add(info.C, int(in.C()))
case FormatABx:
add(info.B, int(in.Bx()))
default:
add(info.B, int(in.SBx()))
}
return strings.Join(parts, " ")
}
func operand(m *Module, f *Function, pc int, k Operand, v int) string {
n := strconv.Itoa(v)
switch k {
case OpReg:
return "r" + n
case OpInt, OpCount, OpField:
return n
case OpBool:
return strconv.FormatBool(v != 0)
case OpLane:
return "." + "xyzw"[v&3:v&3+1]
case OpConst:
if v < len(f.Consts) {
return "k" + n + "(" + f.Consts[v].String() + ")"
}
return "k" + n + "(?)"
case OpJump:
return "-> " + strconv.Itoa(pc+1+v)
case OpFunc:
if v < len(m.Functions) {
return "f" + n + "(" + m.Functions[v].Name + ")"
}
return "f" + n
case OpExtern:
if v < len(m.Externs) {
return "x" + n + "(" + m.Externs[v].Package + "." + m.Externs[v].Name + ")"
}
return "x" + n
case OpHost:
if v < len(m.Imports) {
return "h" + n + "(" + m.Imports[v].Module + "." + m.Imports[v].Name + ")"
}
return "h" + n
case OpSelf:
return "self[" + n + "]"
case OpType:
return "t" + n
case OpYield:
if v < len(WaitNames) {
return WaitNames[v]
}
return "wait" + n
case OpSignal:
if f.Script >= 0 && int(f.Script) < len(m.Scripts) && v < len(m.Scripts[f.Script].Signals) {
return "g" + n + "(" + m.Scripts[f.Script].Signals[v].Name + ")"
}
return "g" + n
case OpConnect:
if v < len(m.Connects) {
c := m.Connects[v]
return "c" + n + "(" + short(c.Source) + "." + c.Signal + " -> " + short(c.Target) + "." + c.Handler + ")"
}
return "c" + n
case OpScriptRef:
if v < len(m.ScriptRefs) {
return "s" + n + "(" + m.ScriptRefs[v] + ")"
}
return "s" + n
case OpXField:
if v < len(m.XFields) {
x := m.XFields[v]
if int(x.Ref) < len(m.ScriptRefs) {
return "f" + n + "(" + short(m.ScriptRefs[x.Ref]) + "." + x.Name + ")"
}
}
return "f" + n
case OpXMethod:
if v < len(m.XMethods) {
x := m.XMethods[v]
if int(x.Ref) < len(m.ScriptRefs) {
return "m" + n + "(" + short(m.ScriptRefs[x.Ref]) + "." + x.Name + ")"
}
}
return "m" + n
}
return ""
}