forked from TrueCloudLab/neoneo-go
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.
This commit is contained in:
parent
4eae55143f
commit
7a6d6f43ce
1 changed files with 30 additions and 23 deletions
53
pkg/vm/vm.go
53
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))
|
v.estack.PushVal(len(arr))
|
||||||
|
|
||||||
case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT:
|
case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT:
|
||||||
var (
|
offset := v.getJumpOffset(ctx, parameter)
|
||||||
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))
|
|
||||||
}
|
|
||||||
cond := true
|
cond := true
|
||||||
if op > opcode.JMP {
|
if op != opcode.JMP {
|
||||||
cond = v.estack.Pop().Bool()
|
cond = v.estack.Pop().Bool() == (op == opcode.JMPIF)
|
||||||
if op == opcode.JMPIFNOT {
|
|
||||||
cond = !cond
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cond {
|
|
||||||
ctx.nextip = offset
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v.jumpIf(ctx, offset, cond)
|
||||||
|
|
||||||
case opcode.CALL:
|
case opcode.CALL:
|
||||||
v.checkInvocationStackSize()
|
v.checkInvocationStackSize()
|
||||||
|
|
||||||
newCtx := ctx.Copy()
|
newCtx := ctx.Copy()
|
||||||
newCtx.rvcount = -1
|
newCtx.rvcount = -1
|
||||||
v.istack.PushVal(newCtx)
|
v.istack.PushVal(newCtx)
|
||||||
err = v.execute(v.Context(), opcode.JMP, parameter)
|
|
||||||
if err != nil {
|
offset := v.getJumpOffset(newCtx, parameter)
|
||||||
return
|
v.jumpIf(newCtx, offset, true)
|
||||||
}
|
|
||||||
|
|
||||||
case opcode.SYSCALL:
|
case opcode.SYSCALL:
|
||||||
interopID := GetInteropID(parameter)
|
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.estack = newCtx.estack
|
||||||
v.astack = newCtx.astack
|
v.astack = newCtx.astack
|
||||||
if op == opcode.CALLI {
|
if op == opcode.CALLI {
|
||||||
err = v.execute(v.Context(), opcode.JMP, parameter[2:])
|
offset := v.getJumpOffset(newCtx, parameter[2:])
|
||||||
if err != nil {
|
v.jumpIf(newCtx, offset, true)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case opcode.THROW:
|
case opcode.THROW:
|
||||||
|
@ -1421,6 +1408,26 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
return
|
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 {
|
func cloneIfStruct(item StackItem) StackItem {
|
||||||
switch it := item.(type) {
|
switch it := item.(type) {
|
||||||
case *StructItem:
|
case *StructItem:
|
||||||
|
|
Loading…
Reference in a new issue