Documentation ¶
Overview ¶
Package stack provides stack-related "special opcodes" for use in specops code.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Transform ¶
func Transform(depth uint8) func(indices ...uint8) *Transformation
Transform returns a function that generates a general-purpose Transformation. The `depth` specifies how deep in the stack the Transformation must modify. The indices MUST all be less than `depth`.
Note that the same indices with different depth will result in *different* stack outputs. See Transformation examples.
Types ¶
type ExpectDepth ¶
type ExpectDepth uint
ExpectDepth is a sentinel value that singals to Code.Compile() that it must assert the expected stack depth, returning an error if incorrect. See SetDepth() for caveats; note that the expectation is with respect to specops.Code.Compile() and has nothing to do with concrete (runtime) depths.
func (ExpectDepth) Bytecode ¶
func (d ExpectDepth) Bytecode() ([]byte, error)
Bytecode always returns an error.
type SetDepth ¶
type SetDepth uint
SetDepth is a sentinel value that signals to specops.Code.Compile() that it must modify its internal counter reflecting the current stack depth.
For each vm.OpCode that it encounters, Code.Compile() adjusts a value that reflects its belief about the stack depth. This is a crude mechanism that only works for non-JUMPing code. The programmer can therefore signal, typically after a JUMPDEST, the actual stack depth.
type Transformation ¶
type Transformation struct {
// contains filtered or unexported fields
}
A Transformation transforms the stack by modifying its order, growing, and/or shrinking it.
Example ¶
egs := []struct { desc string xform *stack.Transformation }{ { desc: "Permute", xform: stack.Permute(2, 0, 3, 1), }, { desc: "Permute via Transform", // Although this is equivalent to Permute(), its verbose, intent // isn't clear, and there are no checks that it's a valid // permutation. xform: stack.Transform(4)(2, 0, 3, 1), }, { desc: "Transform same depth", // Guaranteed *not* to have POPs because all stack items in [0,5) // are used. xform: stack.Transform(5)(4, 0, 2, 2, 3, 1), }, { desc: "Transform greater depth", // Guaranteed to have POPs because, although the same indices as // above, a greater stack depth is being transformed. Stack items // {5,6} need to be removed. xform: stack.Transform(7)(4, 0, 2, 2, 3, 1), }, { desc: "Noop Transform", xform: stack.Transform(2)(0, 1), }, { desc: "Noop Permute", xform: stack.Permute(0, 1, 2, 3, 4, 5), }, } for _, eg := range egs { bytecode, err := eg.xform.Bytecode() if err != nil { log.Fatalf("%s error %v", eg.desc, err) } ops := make([]vm.OpCode, len(bytecode)) for i, b := range bytecode { ops[i] = vm.OpCode(b) } fmt.Println(eg.desc, ops) }
Output: Permute [SWAP1 SWAP3 SWAP2] Permute via Transform [SWAP1 SWAP3 SWAP2] Transform same depth [DUP3 SWAP2 SWAP5] Transform greater depth [SWAP2 SWAP3 SWAP5 POP SWAP5 POP DUP2 SWAP3] Noop Transform [] Noop Permute []
func Permute ¶
func Permute(indices ...uint8) *Transformation
Permute returns a Transformation that permutes the order of the stack. The indices MUST be a contiguous set of distinct values [0,n) in any order.
While permutations can also be achieved with Transform(), Permute performs additional checks on the indices (guaranteeing only SWAPs) and signals intent to the reader of the code. See Transformation examples.
func (*Transformation) Bytecode ¶
func (t *Transformation) Bytecode() ([]byte, error)
Bytecode returns the stack-transforming opcodes (SWAP, DUP, etc) necessary to achieve the transformation in the most efficient manner.
func (*Transformation) WithOps ¶
func (t *Transformation) WithOps(ops ...types.OpCode) *Transformation
WithOps sets the exact opcodes that t.Bytecode() MUST return. Possible use cases include:
- Caching: worst-case performance of Permute() is n! while worst-case Transform() may be higher. WithOps is linear in the number of ops.
- Intent signalling: if an exact sequence of opcodes is required but they are opaque, the Transformation setup will inform the reader of the outcome.
When Bytecode() is called on the returned value, it confirms that the ops result in the expected transformation and then returns them verbatim.
WithOps modifies t and then returns it.