package code import ( "encoding/binary" "fmt" "math" ) type Builder struct { code []byte debugInfo DebugInfo relativePcRefs map[int][]int currentLine int lineStart int } func NewBuilder() Builder { return Builder{ code: make([]byte, 0, 64), debugInfo: NewDebugInfo("unknown file"), relativePcRefs: make(map[int][]int), lineStart: -1, currentLine: -1, } } func (b *Builder) AppendOp(op Op) { b.code = append(b.code, byte(op)) } func (b *Builder) AppendInt(x int64) { b.code = append(b.code, make([]byte, 8)...) binary.LittleEndian.PutUint64(b.code[len(b.code)-8:], uint64(x)) } func (b *Builder) AppendFloat(x float64) { b.AppendInt(int64(math.Float64bits(x))) } func (b *Builder) AppendString(s string) { b.code = append(b.code, []byte(s)...) b.code = append(b.code, 0) } func (b *Builder) AppendReferenceToPc(pc int64) { b.addPcRef(int(pc), b.Len()) b.AppendInt(pc) } func (b *Builder) AppendRaw(raw Raw) { b.code = append(b.code, raw...) } func (b *Builder) AppendBuilder(other Builder) { code := other.code for pc, refsToPc := range other.relativePcRefs { newPc := b.Len() + pc for _, ref := range refsToPc { valueAtRef := binary.LittleEndian.Uint64(code[ref : ref+8]) if int(valueAtRef) != pc { panic(fmt.Errorf("reference to pc in builder does not actually reference pc. (pc: %d, value at reference: %d)", pc, valueAtRef)) } binary.LittleEndian.PutUint64(code[ref:], uint64(newPc)) b.addPcRef(newPc, ref+b.Len()) } } b.code = append(b.code, code...) if other.debugInfo.pcToLine.Len() != 0 || other.currentLine != -1 || other.lineStart != -1 { panic("appending debug infos not implemented yet") } } func (b *Builder) AppendBuilderWithoutAdjustingReferences(other Builder) { code := other.code for pc, refsToPc := range other.relativePcRefs { for _, ref := range refsToPc { b.addPcRef(pc, ref+b.Len()) } } b.code = append(b.code, code...) if other.debugInfo.pcToLine.Len() != 0 || other.currentLine != -1 || other.lineStart != -1 { panic("appending debug infos not implemented yet") } } func (b *Builder) StartLine(line int) { if b.lineStart != -1 { panic("line already started") } b.currentLine = line b.lineStart = b.Len() } func (b *Builder) EndLine() { defer func() { b.currentLine = -1 b.lineStart = -1 }() if b.lineStart == -1 { panic("line not started") } if b.lineStart == b.Len() { return } b.debugInfo.AppendLine(b.Len()-1, b.currentLine) } func (b *Builder) SetInt(at int, x int64) { binary.LittleEndian.PutUint64(b.code[at:], uint64(x)) } func (b *Builder) Code() Raw { return Raw(b.code) } func (b *Builder) Len() int { return len(b.code) } func (b *Builder) Build() Code { return New(b.code, b.debugInfo) } func (b *Builder) addPcRef(pc int, at int) { refs, ok := b.relativePcRefs[pc] if ok { refs = append(refs, at) } else { refs = make([]int, 1) refs[0] = at } b.relativePcRefs[pc] = refs }