Page 153 - 《软件学报》2021年第6期
P. 153

韩宁  等:以太坊中间语言的可执行语义                                                              1727


             9.         let blk′=(n_expr_lst[EGasPush,EGasJump,EGasJumpDest] EGasPush|rtl|) ++ blk  blk ++ es  post_es in
             10.          [〈((blk′,(zip(map fst ptl) (map peel es))@rzl,fs′,Some (FunctionV f ptl rtl blk)) B
             11.             #((if (|rtl|≠0) then (EList(map(λxt.(ERetId xt)) rtl)) else EStop),ls,fs) E #lstk′),gs,ck〉 N ])
             12.       |Some (CallBuiltinV callgb)⇒(
             13.           if (callgb∈{Call,CallCode,DelegateCall,Return,Revert,Selfdestruct,Stop,Invalid})
             14.           then ((step_callbuiltin〈(EImFunCall f es,ls,fs) E #lstk′,gs,ck〉 N  callgb lits))
             15.           else [ ]
             16.       …
             17.       )
             18. ))
             表达式 EImFunCall f es 的执行首先考虑 es 中是否有未完全求值的参数表达式:若有,则在相应上下文中执
         行最右侧的未完全求值表达式(第 4 行);否则,按照所调用函数是否为自定义函数作不同描述.
             若函数标识符在环境(aggr_fs((EImFunCall f es,ls,fs) E  # lstk′))@builtin_ctx 中被映射为形如 FunctionV f ptl
         rtl  blk 的值(第 8 行),说明所调用函数为自定义函数.此种情况下,向被调函数的函数体 blk 前后分别添加表示调
         用时进行跳转和参数入栈所消耗 gas 的表达式以及表示返回时调整栈布局(包括参数和局部变量出栈等操作)
         所消耗 gas 的表达式,形成代码块 bl′.在 blk′定义中,使用了代码块与表达式列表的连接符++ es 以及表达式列表
         与代码块的连接符++ blk .代码块 blk′放在新的局部栈帧中,准备执行.而调用者栈帧中的表达式置为 if (|rtl|≠0)
         then (EList(map(λxt.(ERetId  xt))  rtl))  else  EStop,表示若返回值个数不为零,则设置相应个数的用于接收返回值
         的占位符;否则,函数调用在该栈帧再次成为栈顶时已结束(EStop).
             若函数标识符在环境(aggr_fs((EImFunCall  f  es,ls,fs) E  #  lstk′))@builtin_ctx 中被映射为形如 CallBuiltinV
         callgb(第 12 行),OpBuiltinV  callgb,RBuiltinV  callgb 等值,则表示所调用函数为相应类型的内置函数.其中,形如
         CallBuiltinV callgb 的值表示与外部调用和返回相关的内置函数,具体是哪个函数,由 callgb 表示.若 callgb 在 Yul
         所支持的此类内置函数范围内,则借助辅助函数 step_callbuiltin 给出调用的执行结果(略去进一步技术细节);否
         则,给出表示出错的空全局栈.其余种类的内置函数调用作类似描述.
             若外部调用发生后,被调合约正常终止,则全局栈栈顶为形如〈gs′,ret_data,ck′〉 H 的栈帧(对应图 1(G)).在语义
         中,该外部调用返回后的全局栈由 step tre (〈gs′,ret_data,ck′〉 H #gstk′)给出(对某个 gstk′).而该表达式的定义根据外
         部调用的种类(对应于太坊中的 CALL 指令、CALLCODE 指令还是 DELEGATECALL 指令),借助不同辅助函数
         给出.当外部调用由 CALL 指令触发,即 ck′由 CKCall 构造时,step tre (〈gs′,ret_data,ck′〉 H #gstk′)定义为 callret (〈gs′,
         ret_data,ck′〉 H #gstk′),其中,辅助函数 callret 定义如下.
             1.   callret (〈gs′,ret_data,(CKCall g to val io is oo os)〉 H #〈(Blk(e#es),ls,fs,cf) B #lstk′,gs,ck〉 N #gstk′)
             2.      =(let … in
             3.         let flag=if (accs gs to=None) then 0 else 1 in
             4.         let c_call=Cgascap val flag g (gas gs) in
             5.         let c=(Cbase val flag)+(Cmem(uint(naws gs)) (uint naws))+(sint c_call) in
             6.            (〈(Blk((expr_fill_retids e [(extcall_ret_id,((NL 1):L U256))])#es),ls,fs,cf) B #lstk′,
             7.               (gs (| gas:=(gas gs′)+(gas gs)−(word_of_int c),… |) ),ck〉 N #gstk′))
             由于被调合约正常终止,返回值为 1.上述定义中,利用函数 expr_fill_retids 将该返回值写入此前发生调用时
         留在全局栈次栈顶中局部栈栈顶表达式中的辅助变量 extcall_ret_id(第 6 行).定义中,第 3 行~第 5 行计算调用
         的总 gas 成本 c.返回后,全局状态中的 gas 值更新为当前全局栈中的 gas 值与次栈顶中的 gas 值(即被调合约终
         止时的 gas 余量)的和,减去一次调用所消耗的成本 c 所得结果(第 7 行).
             关于其他种类外部调用的正常返回、外部调用的异常返回以及所有其他语义规则,不再详细阐释.
   148   149   150   151   152   153   154   155   156   157   158