OSDN Git Service

feat: add processIssuing (#152)
[bytom/vapor.git] / vendor / github.com / bytom / protocol / vm / vmutil / builder.go
diff --git a/vendor/github.com/bytom/protocol/vm/vmutil/builder.go b/vendor/github.com/bytom/protocol/vm/vmutil/builder.go
new file mode 100644 (file)
index 0000000..8f57cd1
--- /dev/null
@@ -0,0 +1,114 @@
+package vmutil
+
+import (
+       "encoding/binary"
+
+       "github.com/bytom/errors"
+       "github.com/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
+}