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
|
op vm.OpCode
|
||||||
error error
|
error error
|
||||||
started bool
|
started bool
|
||||||
|
eofEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInstructionIterator creates a new instruction iterator.
|
// NewInstructionIterator creates a new instruction iterator.
|
||||||
|
|
@ -41,6 +42,13 @@ func NewInstructionIterator(code []byte) *instructionIterator {
|
||||||
return it
|
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.
|
// Next returns true if there is a next instruction and moves on.
|
||||||
func (it *instructionIterator) Next() bool {
|
func (it *instructionIterator) Next() bool {
|
||||||
if it.error != nil || uint64(len(it.code)) <= it.pc {
|
if it.error != nil || uint64(len(it.code)) <= it.pc {
|
||||||
|
|
@ -63,10 +71,23 @@ func (it *instructionIterator) Next() bool {
|
||||||
// We reached the end.
|
// We reached the end.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
it.op = vm.OpCode(it.code[it.pc])
|
it.op = vm.OpCode(it.code[it.pc])
|
||||||
|
var a int
|
||||||
if a := vm.Immediates(it.op); a > 0 {
|
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)
|
u := it.pc + 1 + uint64(a)
|
||||||
if uint64(len(it.code)) <= it.pc || uint64(len(it.code)) < u {
|
if uint64(len(it.code)) <= it.pc || uint64(len(it.code)) < u {
|
||||||
it.error = fmt.Errorf("incomplete instruction at %v", it.pc)
|
it.error = fmt.Errorf("incomplete instruction at %v", it.pc)
|
||||||
|
|
@ -105,7 +126,6 @@ func PrintDisassembled(code string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
it := NewInstructionIterator(script)
|
it := NewInstructionIterator(script)
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
if it.Arg() != nil && 0 < len(it.Arg()) {
|
if it.Arg() != nil && 0 < len(it.Arg()) {
|
||||||
|
|
|
||||||
|
|
@ -17,44 +17,78 @@
|
||||||
package asm
|
package asm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
|
||||||
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests disassembling instructions
|
// Tests disassembling instructions
|
||||||
func TestInstructionIterator(t *testing.T) {
|
func TestInstructionIterator(t *testing.T) {
|
||||||
for i, tc := range []struct {
|
for i, tc := range []struct {
|
||||||
want int
|
|
||||||
code string
|
code string
|
||||||
wantErr string
|
legacyWant string
|
||||||
|
eofWant string
|
||||||
}{
|
}{
|
||||||
{2, "61000000", ""}, // valid code
|
{"", "", ""}, // empty
|
||||||
{0, "6100", "incomplete instruction at 0"}, // invalid code
|
{"6100", `err: incomplete instruction at 0`, `err: incomplete instruction at 0`},
|
||||||
{2, "5900", ""}, // push0
|
{"61000000", `
|
||||||
{0, "", ""}, // empty
|
00000: PUSH2 0x0000
|
||||||
{2, "d1aabb00", ""}, // DATALOADN(aabb),STOP
|
00003: STOP`, `
|
||||||
{0, "d1aa", "incomplete instruction at 0"}, // DATALOADN(aa) invalid
|
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 (
|
var (
|
||||||
have int
|
|
||||||
code, _ = hex.DecodeString(tc.code)
|
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() {
|
for it.Next() {
|
||||||
have++
|
if it.Arg() != nil && 0 < len(it.Arg()) {
|
||||||
}
|
fmt.Fprintf(out, "%05x: %v %#x\n", it.PC(), it.Op(), it.Arg())
|
||||||
var haveErr = ""
|
} else {
|
||||||
if it.Error() != nil {
|
fmt.Fprintf(out, "%05x: %v\n", it.PC(), it.Op())
|
||||||
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 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 {
|
if op == RJUMPV {
|
||||||
// RJUMPV is unique as it has a variable sized operand.
|
// RJUMPV is unique as it has a variable sized operand.
|
||||||
// The total size is determined by the count byte which
|
// 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
|
// in other validation steps -- for now, just return a
|
||||||
// valid bitmap for as much of the code as is
|
// valid bitmap for as much of the code as is
|
||||||
// available.
|
// available.
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,13 @@ func init() {
|
||||||
|
|
||||||
// Immediates returns the number bytes of immediates (argument not from
|
// Immediates returns the number bytes of immediates (argument not from
|
||||||
// stack but from code) a given opcode has.
|
// 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 {
|
func Immediates(op OpCode) int {
|
||||||
return int(immediates[op])
|
return int(immediates[op])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue