6 "github.com/bytom/vapor/errors"
7 "github.com/bytom/vapor/protocol/vm"
14 // Maps a jump target number to its absolute address.
15 jumpAddr map[int]uint32
17 // Maps a jump target number to the list of places where its
18 // absolute address must be filled in once known.
19 jumpPlaceholders map[int][]int
22 func NewBuilder() *Builder {
24 jumpAddr: make(map[int]uint32),
25 jumpPlaceholders: make(map[int][]int),
29 // AddInt64 adds a pushdata instruction for an integer value.
30 func (b *Builder) AddInt64(n int64) *Builder {
31 b.program = append(b.program, vm.PushdataInt64(n)...)
35 // AddData adds a pushdata instruction for a given byte string.
36 func (b *Builder) AddData(data []byte) *Builder {
37 b.program = append(b.program, vm.PushdataBytes(data)...)
41 // AddRawBytes simply appends the given bytes to the program. (It does
42 // not introduce a pushdata opcode.)
43 func (b *Builder) AddRawBytes(data []byte) *Builder {
44 b.program = append(b.program, data...)
48 // AddOp adds the given opcode to the program.
49 func (b *Builder) AddOp(op vm.Op) *Builder {
50 b.program = append(b.program, byte(op))
54 // NewJumpTarget allocates a number that can be used as a jump target
55 // in AddJump and AddJumpIf. Call SetJumpTarget to associate the
56 // number with a program location.
57 func (b *Builder) NewJumpTarget() int {
62 // AddJump adds a JUMP opcode whose target is the given target
63 // number. The actual program location of the target does not need to
64 // be known yet, as long as SetJumpTarget is called before Build.
65 func (b *Builder) AddJump(target int) *Builder {
66 return b.addJump(vm.OP_JUMP, target)
69 // AddJump adds a JUMPIF opcode whose target is the given target
70 // number. The actual program location of the target does not need to
71 // be known yet, as long as SetJumpTarget is called before Build.
72 func (b *Builder) AddJumpIf(target int) *Builder {
73 return b.addJump(vm.OP_JUMPIF, target)
76 func (b *Builder) addJump(op vm.Op, target int) *Builder {
78 b.jumpPlaceholders[target] = append(b.jumpPlaceholders[target], len(b.program))
79 b.AddRawBytes([]byte{0, 0, 0, 0})
83 // SetJumpTarget associates the given jump-target number with the
84 // current position in the program - namely, the program's length,
85 // such that the first instruction executed by a jump using this
86 // target will be whatever instruction is added next. It is legal for
87 // SetJumpTarget to be called at the end of the program, causing jumps
88 // using that target to fall off the end. There must be a call to
89 // SetJumpTarget for every jump target used before any call to Build.
90 func (b *Builder) SetJumpTarget(target int) *Builder {
91 b.jumpAddr[target] = uint32(len(b.program))
95 var ErrUnresolvedJump = errors.New("unresolved jump target")
97 // Build produces the bytecode of the program. It first resolves any
98 // jumps in the program by filling in the addresses of their
99 // targets. This requires SetJumpTarget to be called prior to Build
100 // for each jump target used (in a call to AddJump or AddJumpIf). If
101 // any target's address hasn't been set in this way, this function
102 // produces ErrUnresolvedJump. There are no other error conditions.
103 func (b *Builder) Build() ([]byte, error) {
104 for target, placeholders := range b.jumpPlaceholders {
105 addr, ok := b.jumpAddr[target]
107 return nil, errors.Wrapf(ErrUnresolvedJump, "target %d", target)
109 for _, placeholder := range placeholders {
110 binary.LittleEndian.PutUint32(b.program[placeholder:placeholder+4], addr)
113 return b.program, nil