tail call recursion with continuation passing
This commit is contained in:
parent
4c0ba876ef
commit
e5cca19e7f
|
@ -34,6 +34,9 @@ proc newBaseComputation*(vmState: BaseVMState, blockNumber: UInt256, message: Me
|
||||||
else:
|
else:
|
||||||
blockNumber.toFork.forkToSchedule
|
blockNumber.toFork.forkToSchedule
|
||||||
result.forkOverride = forkOverride
|
result.forkOverride = forkOverride
|
||||||
|
# a dummy/terminus continuation proc
|
||||||
|
result.nextProc = proc() =
|
||||||
|
discard
|
||||||
|
|
||||||
proc isOriginComputation*(c: BaseComputation): bool =
|
proc isOriginComputation*(c: BaseComputation): bool =
|
||||||
# Is this computation the computation initiated by a transaction
|
# Is this computation the computation initiated by a transaction
|
||||||
|
@ -168,6 +171,8 @@ proc transferBalance(computation: BaseComputation, opCode: static[Op]) =
|
||||||
db.addBalance(computation.msg.storageAddress, computation.msg.value)
|
db.addBalance(computation.msg.storageAddress, computation.msg.value)
|
||||||
|
|
||||||
template continuation*(comp: BaseComputation, body: untyped) =
|
template continuation*(comp: BaseComputation, body: untyped) =
|
||||||
|
# this is a helper template to implement continuation
|
||||||
|
# passing and convert all recursion into tail call
|
||||||
var tmpNext = comp.nextProc
|
var tmpNext = comp.nextProc
|
||||||
comp.nextProc = proc() =
|
comp.nextProc = proc() =
|
||||||
body
|
body
|
||||||
|
@ -189,21 +194,27 @@ proc executeOpcodes*(computation: BaseComputation) {.gcsafe.}
|
||||||
|
|
||||||
proc applyMessage*(computation: BaseComputation, opCode: static[Op]) =
|
proc applyMessage*(computation: BaseComputation, opCode: static[Op]) =
|
||||||
computation.snapshot()
|
computation.snapshot()
|
||||||
defer: computation.dispose()
|
defer:
|
||||||
|
computation.dispose()
|
||||||
|
|
||||||
when opCode in {CallCode, Call, Create}:
|
when opCode in {CallCode, Call, Create}:
|
||||||
computation.transferBalance(opCode)
|
computation.transferBalance(opCode)
|
||||||
if computation.isError():
|
if computation.isError():
|
||||||
computation.rollback()
|
computation.rollback()
|
||||||
|
computation.nextProc()
|
||||||
return
|
return
|
||||||
|
|
||||||
if computation.gasMeter.gasRemaining < 0:
|
if computation.gasMeter.gasRemaining < 0:
|
||||||
computation.commit()
|
computation.commit()
|
||||||
|
computation.nextProc()
|
||||||
return
|
return
|
||||||
|
|
||||||
executeOpcodes(computation)
|
continuation(computation):
|
||||||
postExecuteVM(computation)
|
postExecuteVM(computation)
|
||||||
|
|
||||||
|
executeOpcodes(computation)
|
||||||
|
computation.nextProc()
|
||||||
|
|
||||||
proc addChildComputation*(computation: BaseComputation, child: BaseComputation) =
|
proc addChildComputation*(computation: BaseComputation, child: BaseComputation) =
|
||||||
if child.isError:
|
if child.isError:
|
||||||
if child.msg.isCreate:
|
if child.msg.isCreate:
|
||||||
|
|
|
@ -593,7 +593,7 @@ op create, inline = false, value, startPosition, size:
|
||||||
var childComp = setupCreate(computation, memPos, len, value)
|
var childComp = setupCreate(computation, memPos, len, value)
|
||||||
if childComp.isNil: return
|
if childComp.isNil: return
|
||||||
|
|
||||||
childComp.applyMessage(Create)
|
continuation(childComp):
|
||||||
computation.addChildComputation(childComp)
|
computation.addChildComputation(childComp)
|
||||||
|
|
||||||
if childComp.isError:
|
if childComp.isError:
|
||||||
|
@ -601,6 +601,8 @@ op create, inline = false, value, startPosition, size:
|
||||||
else:
|
else:
|
||||||
push: childComp.msg.storageAddress
|
push: childComp.msg.storageAddress
|
||||||
|
|
||||||
|
childComp.applyMessage(Create)
|
||||||
|
|
||||||
proc callParams(computation: BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, MsgFlags) =
|
proc callParams(computation: BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, MsgFlags) =
|
||||||
let gas = computation.stack.popInt()
|
let gas = computation.stack.popInt()
|
||||||
let codeAddress = computation.stack.popAddress()
|
let codeAddress = computation.stack.popAddress()
|
||||||
|
@ -683,7 +685,7 @@ proc staticCallParams(computation: BaseComputation): (UInt256, UInt256, EthAddre
|
||||||
emvcStatic) # is_static
|
emvcStatic) # is_static
|
||||||
|
|
||||||
template genCall(callName: untyped, opCode: Op): untyped =
|
template genCall(callName: untyped, opCode: Op): untyped =
|
||||||
proc `callName Setup`(computation: BaseComputation, callNameStr: string): (BaseComputation, int, int) =
|
proc `callName Setup`(computation: BaseComputation, callNameStr: string): BaseComputation =
|
||||||
let (gas, value, to, sender,
|
let (gas, value, to, sender,
|
||||||
codeAddress,
|
codeAddress,
|
||||||
memoryInputStartPosition, memoryInputSize,
|
memoryInputStartPosition, memoryInputSize,
|
||||||
|
@ -750,16 +752,18 @@ template genCall(callName: untyped, opCode: Op): untyped =
|
||||||
childMsg,
|
childMsg,
|
||||||
some(computation.getFork))
|
some(computation.getFork))
|
||||||
|
|
||||||
result = (childComp, memOutPos, memOutLen)
|
computation.memOutPos = memOutPos
|
||||||
|
computation.memOutLen = memOutLen
|
||||||
|
result = childComp
|
||||||
|
|
||||||
op callName, inline = false:
|
op callName, inline = false:
|
||||||
## CALL, 0xf1, Message-Call into an account
|
## CALL, 0xf1, Message-Call into an account
|
||||||
## CALLCODE, 0xf2, Message-call into this account with an alternative account's code.
|
## CALLCODE, 0xf2, Message-call into this account with an alternative account's code.
|
||||||
## DELEGATECALL, 0xf4, Message-call into this account with an alternative account's code, but persisting the current values for sender and value.
|
## DELEGATECALL, 0xf4, Message-call into this account with an alternative account's code, but persisting the current values for sender and value.
|
||||||
## STATICCALL, 0xfa, Static message-call into an account.
|
## STATICCALL, 0xfa, Static message-call into an account.
|
||||||
var (childComp, memOutPos, memOutLen) = `callName Setup`(computation, callName.astToStr)
|
var childComp = `callName Setup`(computation, callName.astToStr)
|
||||||
|
|
||||||
childComp.applyMessage(opCode)
|
continuation(childComp):
|
||||||
addChildComputation(computation, childComp)
|
addChildComputation(computation, childComp)
|
||||||
|
|
||||||
if childComp.isError:
|
if childComp.isError:
|
||||||
|
@ -768,11 +772,13 @@ template genCall(callName: untyped, opCode: Op): untyped =
|
||||||
push: 1
|
push: 1
|
||||||
|
|
||||||
if not childComp.shouldEraseReturnData:
|
if not childComp.shouldEraseReturnData:
|
||||||
let actualOutputSize = min(memOutLen, childComp.output.len)
|
let actualOutputSize = min(computation.memOutLen, childComp.output.len)
|
||||||
computation.memory.write(
|
computation.memory.write(
|
||||||
memOutPos,
|
computation.memOutPos,
|
||||||
childComp.output.toOpenArray(0, actualOutputSize - 1))
|
childComp.output.toOpenArray(0, actualOutputSize - 1))
|
||||||
|
|
||||||
|
childComp.applyMessage(opCode)
|
||||||
|
|
||||||
genCall(call, Call)
|
genCall(call, Call)
|
||||||
genCall(callCode, CallCode)
|
genCall(callCode, CallCode)
|
||||||
genCall(delegateCall, DelegateCall)
|
genCall(delegateCall, DelegateCall)
|
||||||
|
|
|
@ -67,7 +67,10 @@ type
|
||||||
dbsnapshot*: Snapshot
|
dbsnapshot*: Snapshot
|
||||||
instr*: Op
|
instr*: Op
|
||||||
opIndex*: int
|
opIndex*: int
|
||||||
|
# continuation helpers
|
||||||
nextProc*: proc()
|
nextProc*: proc()
|
||||||
|
memOutLen*: int
|
||||||
|
memOutPos*: int
|
||||||
|
|
||||||
Error* = ref object
|
Error* = ref object
|
||||||
info*: string
|
info*: string
|
||||||
|
|
Loading…
Reference in New Issue