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:
|
||||
blockNumber.toFork.forkToSchedule
|
||||
result.forkOverride = forkOverride
|
||||
# a dummy/terminus continuation proc
|
||||
result.nextProc = proc() =
|
||||
discard
|
||||
|
||||
proc isOriginComputation*(c: BaseComputation): bool =
|
||||
# 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)
|
||||
|
||||
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
|
||||
comp.nextProc = proc() =
|
||||
body
|
||||
|
@ -189,20 +194,26 @@ proc executeOpcodes*(computation: BaseComputation) {.gcsafe.}
|
|||
|
||||
proc applyMessage*(computation: BaseComputation, opCode: static[Op]) =
|
||||
computation.snapshot()
|
||||
defer: computation.dispose()
|
||||
defer:
|
||||
computation.dispose()
|
||||
|
||||
when opCode in {CallCode, Call, Create}:
|
||||
computation.transferBalance(opCode)
|
||||
if computation.isError():
|
||||
computation.rollback()
|
||||
computation.nextProc()
|
||||
return
|
||||
|
||||
if computation.gasMeter.gasRemaining < 0:
|
||||
computation.commit()
|
||||
computation.nextProc()
|
||||
return
|
||||
|
||||
continuation(computation):
|
||||
postExecuteVM(computation)
|
||||
|
||||
executeOpcodes(computation)
|
||||
postExecuteVM(computation)
|
||||
computation.nextProc()
|
||||
|
||||
proc addChildComputation*(computation: BaseComputation, child: BaseComputation) =
|
||||
if child.isError:
|
||||
|
|
|
@ -593,13 +593,15 @@ op create, inline = false, value, startPosition, size:
|
|||
var childComp = setupCreate(computation, memPos, len, value)
|
||||
if childComp.isNil: return
|
||||
|
||||
childComp.applyMessage(Create)
|
||||
computation.addChildComputation(childComp)
|
||||
continuation(childComp):
|
||||
computation.addChildComputation(childComp)
|
||||
|
||||
if childComp.isError:
|
||||
push: 0
|
||||
else:
|
||||
push: childComp.msg.storageAddress
|
||||
if childComp.isError:
|
||||
push: 0
|
||||
else:
|
||||
push: childComp.msg.storageAddress
|
||||
|
||||
childComp.applyMessage(Create)
|
||||
|
||||
proc callParams(computation: BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, MsgFlags) =
|
||||
let gas = computation.stack.popInt()
|
||||
|
@ -683,7 +685,7 @@ proc staticCallParams(computation: BaseComputation): (UInt256, UInt256, EthAddre
|
|||
emvcStatic) # is_static
|
||||
|
||||
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,
|
||||
codeAddress,
|
||||
memoryInputStartPosition, memoryInputSize,
|
||||
|
@ -750,28 +752,32 @@ template genCall(callName: untyped, opCode: Op): untyped =
|
|||
childMsg,
|
||||
some(computation.getFork))
|
||||
|
||||
result = (childComp, memOutPos, memOutLen)
|
||||
computation.memOutPos = memOutPos
|
||||
computation.memOutLen = memOutLen
|
||||
result = childComp
|
||||
|
||||
op callName, inline = false:
|
||||
## CALL, 0xf1, Message-Call into an account
|
||||
## 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.
|
||||
## STATICCALL, 0xfa, Static message-call into an account.
|
||||
var (childComp, memOutPos, memOutLen) = `callName Setup`(computation, callName.astToStr)
|
||||
var childComp = `callName Setup`(computation, callName.astToStr)
|
||||
|
||||
continuation(childComp):
|
||||
addChildComputation(computation, childComp)
|
||||
|
||||
if childComp.isError:
|
||||
push: 0
|
||||
else:
|
||||
push: 1
|
||||
|
||||
if not childComp.shouldEraseReturnData:
|
||||
let actualOutputSize = min(computation.memOutLen, childComp.output.len)
|
||||
computation.memory.write(
|
||||
computation.memOutPos,
|
||||
childComp.output.toOpenArray(0, actualOutputSize - 1))
|
||||
|
||||
childComp.applyMessage(opCode)
|
||||
addChildComputation(computation, childComp)
|
||||
|
||||
if childComp.isError:
|
||||
push: 0
|
||||
else:
|
||||
push: 1
|
||||
|
||||
if not childComp.shouldEraseReturnData:
|
||||
let actualOutputSize = min(memOutLen, childComp.output.len)
|
||||
computation.memory.write(
|
||||
memOutPos,
|
||||
childComp.output.toOpenArray(0, actualOutputSize - 1))
|
||||
|
||||
genCall(call, Call)
|
||||
genCall(callCode, CallCode)
|
||||
|
|
|
@ -67,7 +67,10 @@ type
|
|||
dbsnapshot*: Snapshot
|
||||
instr*: Op
|
||||
opIndex*: int
|
||||
# continuation helpers
|
||||
nextProc*: proc()
|
||||
memOutLen*: int
|
||||
memOutPos*: int
|
||||
|
||||
Error* = ref object
|
||||
info*: string
|
||||
|
|
Loading…
Reference in New Issue