about summary refs log tree commit diff
path: root/pkg/lang/vm/code/builder.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/lang/vm/code/builder.go')
-rw-r--r--pkg/lang/vm/code/builder.go108
1 files changed, 58 insertions, 50 deletions
diff --git a/pkg/lang/vm/code/builder.go b/pkg/lang/vm/code/builder.go
index f027c49..adb2eed 100644
--- a/pkg/lang/vm/code/builder.go
+++ b/pkg/lang/vm/code/builder.go
@@ -2,14 +2,15 @@ package code
 
 import (
 	"encoding/binary"
-	"fmt"
 	"math"
 )
 
 type Builder struct {
-	code           []byte
-	debugInfo      DebugInfo
-	relativePcRefs map[int][]int
+	code      []byte
+	debugInfo DebugInfo
+
+	markers    map[Marker]int
+	markerRefs map[int]Marker
 
 	currentLine int
 	lineStart   int
@@ -17,11 +18,14 @@ type Builder struct {
 
 func NewBuilder() Builder {
 	return Builder{
-		code:           make([]byte, 0, 64),
-		debugInfo:      NewDebugInfo("unknown file"),
-		relativePcRefs: make(map[int][]int),
-		lineStart:      -1,
-		currentLine:    -1,
+		code:      make([]byte, 0, 64),
+		debugInfo: NewDebugInfo("unknown file"),
+
+		markers:    make(map[Marker]int, 0),
+		markerRefs: make(map[int]Marker, 0),
+
+		lineStart:   -1,
+		currentLine: -1,
 	}
 }
 
@@ -43,50 +47,45 @@ func (b *Builder) AppendString(s string) {
 	b.code = append(b.code, 0)
 }
 
-func (b *Builder) AppendReferenceToPc(pc int64) {
-	b.addPcRef(int(pc), b.Len())
-	b.AppendInt(pc)
+const UnfilledMarkerReference = 0x11_5AB07A6ED_600D5
+
+func (b *Builder) AppendMarkerReference(marker Marker) {
+	b.markerRefs[b.Len()] = marker
+	b.AppendInt(UnfilledMarkerReference)
 }
 
 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))
+func (b *Builder) AppendBuilder(other Builder) error {
+	for marker, pc := range other.markers {
+		if parentPc, ok := b.markers[marker]; ok {
+			return ErrBuilderAppendMarkerDefinitionClash{
+				Marker:   marker,
+				ParentPc: parentPc,
+				ChildPc:  pc,
 			}
-
-			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")
+		b.SetMarker(marker, pc)
 	}
-}
 
-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())
-		}
+	for pc, marker := range other.markerRefs {
+		b.markerRefs[b.Len()+pc] = marker
 	}
 
-	b.code = append(b.code, code...)
+	b.AppendRaw(other.code)
 
-	if other.debugInfo.pcToLine.Len() != 0 || other.currentLine != -1 || other.lineStart != -1 {
-		panic("appending debug infos not implemented yet")
-	}
+	return nil
+}
+
+func (b *Builder) PutMarker(marker Marker) {
+	b.SetMarker(marker, b.Len())
+}
+
+func (b *Builder) SetMarker(marker Marker, pc int) {
+	b.markers[marker] = pc
 }
 
 func (b *Builder) StartLine(line int) {
@@ -127,18 +126,27 @@ func (b *Builder) Len() int {
 	return len(b.code)
 }
 
-func (b *Builder) Build() Code {
-	return New(b.code, b.debugInfo)
-}
+func (b *Builder) Build() (Code, error) {
+	for pc, marker := range b.markerRefs {
+		markerPc, ok := b.markers[marker]
+		if !ok {
+			return Code{}, ErrBuilderUnfulfilledMarker{
+				Marker: marker,
+				Pc:     pc,
+			}
+		}
+
+		valueAtRefPc := binary.LittleEndian.Uint64(b.code[pc : pc+8])
+		if valueAtRefPc != UnfilledMarkerReference {
+			return Code{}, ErrBuilderOverwrittenMarkerReference{
+				Marker:          marker,
+				Pc:              pc,
+				UnexpectedValue: valueAtRefPc,
+			}
+		}
 
-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.SetInt(pc, int64(markerPc))
 	}
 
-	b.relativePcRefs[pc] = refs
+	return New(b.code, b.debugInfo), nil
 }