From 7a6d6f43cea0ea2f6e0715479d1465b0c150a191 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 21 Jan 2020 13:48:59 +0300 Subject: [PATCH] vm: get rid of recursive (*VM).execute calls Recursive execute() calls can affect gas calculation. This commit makes execute() be called only for real opcodes and moves duplicate logic for CALL/JMP into a separate function. --- pkg/vm/vm.go | 53 +++++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index a360f9158..086ab630b 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -1105,34 +1105,23 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro v.estack.PushVal(len(arr)) case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT: - var ( - rOffset = int16(binary.LittleEndian.Uint16(parameter)) - offset = ctx.ip + int(rOffset) - ) - if offset < 0 || offset > len(ctx.prog) { - panic(fmt.Sprintf("JMP: invalid offset %d ip at %d", offset, ctx.ip)) - } + offset := v.getJumpOffset(ctx, parameter) cond := true - if op > opcode.JMP { - cond = v.estack.Pop().Bool() - if op == opcode.JMPIFNOT { - cond = !cond - } - } - if cond { - ctx.nextip = offset + if op != opcode.JMP { + cond = v.estack.Pop().Bool() == (op == opcode.JMPIF) } + v.jumpIf(ctx, offset, cond) + case opcode.CALL: v.checkInvocationStackSize() newCtx := ctx.Copy() newCtx.rvcount = -1 v.istack.PushVal(newCtx) - err = v.execute(v.Context(), opcode.JMP, parameter) - if err != nil { - return - } + + offset := v.getJumpOffset(newCtx, parameter) + v.jumpIf(newCtx, offset, true) case opcode.SYSCALL: interopID := GetInteropID(parameter) @@ -1401,10 +1390,8 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro v.estack = newCtx.estack v.astack = newCtx.astack if op == opcode.CALLI { - err = v.execute(v.Context(), opcode.JMP, parameter[2:]) - if err != nil { - return - } + offset := v.getJumpOffset(newCtx, parameter[2:]) + v.jumpIf(newCtx, offset, true) } case opcode.THROW: @@ -1421,6 +1408,26 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro return } +// jumpIf performs jump to offset if cond is true. +func (v *VM) jumpIf(ctx *Context, offset int, cond bool) { + if cond { + ctx.nextip = offset + } +} + +// getJumpOffset returns instruction number in a current context +// to a which JMP should be performed. +// parameter is interpreted as little-endian int16. +func (v *VM) getJumpOffset(ctx *Context, parameter []byte) int { + rOffset := int16(binary.LittleEndian.Uint16(parameter)) + offset := ctx.ip + int(rOffset) + if offset < 0 || offset > len(ctx.prog) { + panic(fmt.Sprintf("JMP: invalid offset %d ip at %d", offset, ctx.ip)) + } + + return offset +} + func cloneIfStruct(item StackItem) StackItem { switch it := item.(type) { case *StructItem: