mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
core/asm: separate legacy and eof instruction iterators
This commit is contained in:
parent
edec35510e
commit
ae12e05ef6
4 changed files with 97 additions and 36 deletions
|
|
@ -32,6 +32,7 @@ type instructionIterator struct {
|
|||
op vm.OpCode
|
||||
error error
|
||||
started bool
|
||||
eofEnabled bool
|
||||
}
|
||||
|
||||
// NewInstructionIterator creates a new instruction iterator.
|
||||
|
|
@ -41,6 +42,13 @@ func NewInstructionIterator(code []byte) *instructionIterator {
|
|||
return it
|
||||
}
|
||||
|
||||
// NewEOFInstructionIterator creates a new instruction iterator for EOF-code.
|
||||
func NewEOFInstructionIterator(code []byte) *instructionIterator {
|
||||
it := NewInstructionIterator(code)
|
||||
it.eofEnabled = true
|
||||
return it
|
||||
}
|
||||
|
||||
// Next returns true if there is a next instruction and moves on.
|
||||
func (it *instructionIterator) Next() bool {
|
||||
if it.error != nil || uint64(len(it.code)) <= it.pc {
|
||||
|
|
@ -63,10 +71,23 @@ func (it *instructionIterator) Next() bool {
|
|||
// We reached the end.
|
||||
return false
|
||||
}
|
||||
|
||||
it.op = vm.OpCode(it.code[it.pc])
|
||||
|
||||
if a := vm.Immediates(it.op); a > 0 {
|
||||
var a int
|
||||
if !it.eofEnabled { // Legacy code
|
||||
if it.op.IsPush() {
|
||||
a = int(it.op) - int(vm.PUSH0)
|
||||
}
|
||||
} else { // EOF code
|
||||
if it.op == vm.RJUMPV {
|
||||
// RJUMPV is unique as it has a variable sized operand. The total size is
|
||||
// determined by the count byte which immediately follows RJUMPV.
|
||||
maxIndex := int(it.code[it.pc+1])
|
||||
a = (maxIndex+1)*2 + 1
|
||||
} else {
|
||||
a = vm.Immediates(it.op)
|
||||
}
|
||||
}
|
||||
if a > 0 {
|
||||
u := it.pc + 1 + uint64(a)
|
||||
if uint64(len(it.code)) <= it.pc || uint64(len(it.code)) < u {
|
||||
it.error = fmt.Errorf("incomplete instruction at %v", it.pc)
|
||||
|
|
@ -105,7 +126,6 @@ func PrintDisassembled(code string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
it := NewInstructionIterator(script)
|
||||
for it.Next() {
|
||||
if it.Arg() != nil && 0 < len(it.Arg()) {
|
||||
|
|
|
|||
|
|
@ -17,44 +17,78 @@
|
|||
package asm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests disassembling instructions
|
||||
func TestInstructionIterator(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
want int
|
||||
code string
|
||||
wantErr string
|
||||
legacyWant string
|
||||
eofWant string
|
||||
}{
|
||||
{2, "61000000", ""}, // valid code
|
||||
{0, "6100", "incomplete instruction at 0"}, // invalid code
|
||||
{2, "5900", ""}, // push0
|
||||
{0, "", ""}, // empty
|
||||
{2, "d1aabb00", ""}, // DATALOADN(aabb),STOP
|
||||
{0, "d1aa", "incomplete instruction at 0"}, // DATALOADN(aa) invalid
|
||||
{"", "", ""}, // empty
|
||||
{"6100", `err: incomplete instruction at 0`, `err: incomplete instruction at 0`},
|
||||
{"61000000", `
|
||||
00000: PUSH2 0x0000
|
||||
00003: STOP`, `
|
||||
00000: PUSH2 0x0000
|
||||
00003: STOP`},
|
||||
{"5F00", `
|
||||
00000: PUSH0
|
||||
00001: STOP`, `
|
||||
00000: PUSH0
|
||||
00001: STOP`},
|
||||
{"d1aabb00", `00000: DATALOADN
|
||||
00001: opcode 0xaa not defined
|
||||
00002: opcode 0xbb not defined
|
||||
00003: STOP`, `
|
||||
00000: DATALOADN 0xaabb
|
||||
00003: STOP`}, // DATALOADN(aabb),STOP
|
||||
{"d1aa", `
|
||||
00000: DATALOADN
|
||||
00001: opcode 0xaa not defined`, "err: incomplete instruction at 0\n"}, // DATALOADN(aa) invalid
|
||||
{"e20211223344556600", `
|
||||
00000: RJUMPV
|
||||
00001: MUL
|
||||
00002: GT
|
||||
00003: opcode 0x22 not defined
|
||||
00004: CALLER
|
||||
00005: DIFFICULTY
|
||||
00006: SSTORE
|
||||
err: incomplete instruction at 7`, `
|
||||
00000: RJUMPV 0x02112233445566
|
||||
00008: STOP`}, // RJUMPV( 6 bytes), STOP
|
||||
|
||||
} {
|
||||
var (
|
||||
have int
|
||||
code, _ = hex.DecodeString(tc.code)
|
||||
it = NewInstructionIterator(code)
|
||||
legacy = strings.TrimSpace(disassembly(NewInstructionIterator(code)))
|
||||
eof = strings.TrimSpace(disassembly(NewEOFInstructionIterator(code)))
|
||||
)
|
||||
if want := strings.TrimSpace(tc.legacyWant); legacy != want {
|
||||
t.Errorf("test %d: wrong (legacy) output. have:\n%q\nwant:\n%q\n", i, legacy, want)
|
||||
}
|
||||
if want := strings.TrimSpace(tc.eofWant); eof != want {
|
||||
t.Errorf("test %d: wrong (eof) output. have:\n%q\nwant:\n%q\n", i, eof, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func disassembly(it *instructionIterator) string {
|
||||
var out = new(strings.Builder)
|
||||
for it.Next() {
|
||||
have++
|
||||
}
|
||||
var haveErr = ""
|
||||
if it.Error() != nil {
|
||||
haveErr = it.Error().Error()
|
||||
}
|
||||
if haveErr != tc.wantErr {
|
||||
t.Errorf("test %d: encountered error: %q want %q", i, haveErr, tc.wantErr)
|
||||
continue
|
||||
}
|
||||
if have != tc.want {
|
||||
t.Errorf("wrong instruction count, have %d want %d", have, tc.want)
|
||||
if it.Arg() != nil && 0 < len(it.Arg()) {
|
||||
fmt.Fprintf(out, "%05x: %v %#x\n", it.PC(), it.Op(), it.Arg())
|
||||
} else {
|
||||
fmt.Fprintf(out, "%05x: %v\n", it.PC(), it.Op())
|
||||
}
|
||||
}
|
||||
if err := it.Error(); err != nil {
|
||||
fmt.Fprintf(out, "err: %v\n", err)
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ func eofCodeBitmapInternal(code, bits bitvec) bitvec {
|
|||
if op == RJUMPV {
|
||||
// RJUMPV is unique as it has a variable sized operand.
|
||||
// The total size is determined by the count byte which
|
||||
// immediate proceeds RJUMPV. Truncation will be caught
|
||||
// immediate follows RJUMPV. Truncation will be caught
|
||||
// in other validation steps -- for now, just return a
|
||||
// valid bitmap for as much of the code as is
|
||||
// available.
|
||||
|
|
|
|||
|
|
@ -58,6 +58,13 @@ func init() {
|
|||
|
||||
// Immediates returns the number bytes of immediates (argument not from
|
||||
// stack but from code) a given opcode has.
|
||||
// OBS:
|
||||
// - This function assumes EOF instruction-set. It cannot be upon in
|
||||
// a. pre-EOF code
|
||||
// b. post-EOF but legacy code
|
||||
// - RJUMPV is unique as it has a variable sized operand. The total size is
|
||||
// determined by the count byte which immediately follows RJUMPV. This method
|
||||
// will return '3' for RJUMPV, which is the minimum.
|
||||
func Immediates(op OpCode) int {
|
||||
return int(immediates[op])
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue