This repository has been archived by the owner on Mar 21, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 359
/
builder.go
114 lines (97 loc) · 3.64 KB
/
builder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package vmutil
import (
"encoding/binary"
"github.com/bytom/bytom/errors"
"github.com/bytom/bytom/protocol/vm"
)
type Builder struct {
program []byte
jumpCounter int
// Maps a jump target number to its absolute address.
jumpAddr map[int]uint32
// Maps a jump target number to the list of places where its
// absolute address must be filled in once known.
jumpPlaceholders map[int][]int
}
func NewBuilder() *Builder {
return &Builder{
jumpAddr: make(map[int]uint32),
jumpPlaceholders: make(map[int][]int),
}
}
// AddInt64 adds a pushdata instruction for an integer value.
func (b *Builder) AddInt64(n int64) *Builder {
b.program = append(b.program, vm.PushdataInt64(n)...)
return b
}
// AddData adds a pushdata instruction for a given byte string.
func (b *Builder) AddData(data []byte) *Builder {
b.program = append(b.program, vm.PushdataBytes(data)...)
return b
}
// AddRawBytes simply appends the given bytes to the program. (It does
// not introduce a pushdata opcode.)
func (b *Builder) AddRawBytes(data []byte) *Builder {
b.program = append(b.program, data...)
return b
}
// AddOp adds the given opcode to the program.
func (b *Builder) AddOp(op vm.Op) *Builder {
b.program = append(b.program, byte(op))
return b
}
// NewJumpTarget allocates a number that can be used as a jump target
// in AddJump and AddJumpIf. Call SetJumpTarget to associate the
// number with a program location.
func (b *Builder) NewJumpTarget() int {
b.jumpCounter++
return b.jumpCounter
}
// AddJump adds a JUMP opcode whose target is the given target
// number. The actual program location of the target does not need to
// be known yet, as long as SetJumpTarget is called before Build.
func (b *Builder) AddJump(target int) *Builder {
return b.addJump(vm.OP_JUMP, target)
}
// AddJump adds a JUMPIF opcode whose target is the given target
// number. The actual program location of the target does not need to
// be known yet, as long as SetJumpTarget is called before Build.
func (b *Builder) AddJumpIf(target int) *Builder {
return b.addJump(vm.OP_JUMPIF, target)
}
func (b *Builder) addJump(op vm.Op, target int) *Builder {
b.AddOp(op)
b.jumpPlaceholders[target] = append(b.jumpPlaceholders[target], len(b.program))
b.AddRawBytes([]byte{0, 0, 0, 0})
return b
}
// SetJumpTarget associates the given jump-target number with the
// current position in the program - namely, the program's length,
// such that the first instruction executed by a jump using this
// target will be whatever instruction is added next. It is legal for
// SetJumpTarget to be called at the end of the program, causing jumps
// using that target to fall off the end. There must be a call to
// SetJumpTarget for every jump target used before any call to Build.
func (b *Builder) SetJumpTarget(target int) *Builder {
b.jumpAddr[target] = uint32(len(b.program))
return b
}
var ErrUnresolvedJump = errors.New("unresolved jump target")
// Build produces the bytecode of the program. It first resolves any
// jumps in the program by filling in the addresses of their
// targets. This requires SetJumpTarget to be called prior to Build
// for each jump target used (in a call to AddJump or AddJumpIf). If
// any target's address hasn't been set in this way, this function
// produces ErrUnresolvedJump. There are no other error conditions.
func (b *Builder) Build() ([]byte, error) {
for target, placeholders := range b.jumpPlaceholders {
addr, ok := b.jumpAddr[target]
if !ok {
return nil, errors.Wrapf(ErrUnresolvedJump, "target %d", target)
}
for _, placeholder := range placeholders {
binary.LittleEndian.PutUint32(b.program[placeholder:placeholder+4], addr)
}
}
return b.program, nil
}