stack

package
v0.0.0-...-8d9cde1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 15, 2024 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

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.

func (SetDepth) Bytecode

func (d SetDepth) Bytecode() ([]byte, error)

Bytecode always returns an error.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL