Page 152 - 《软件学报》2021年第6期
P. 152
1726 Journal of Software 软件学报 Vol.32, No.6, June 2021
调用为对同一合约内部函数的调用或对其他合约的外部调用,调用栈的变化情况分别如 B→C 和 B→F 所示.若
为内部调用(B→C),则形成新的局部栈帧 lfrm″,并在原局部栈帧的代码部分中用形如 ERetId(_,_)的返回值占位
符替换表达式 EImFunCall f es,形成局部栈帧 lfrm′.被调函数可正常执行后返回,弹出局部栈帧 lfrm″,并使用返
回值替换 ERetId(_,_),形成局部栈帧 lfrm′ (C→D);被调函数亦可由于 gas 耗尽等原因抛出异常,从而销毁整个全
1
局栈帧〈lfrm″#lfrm′#lstk,…〉 N ,并代之以表示异常的全局栈帧〈…〉 E (C→E).直观上,后一种情况表示当前合约中所
有的内部调用均异常终止.若表达式 EImFunCall f es 进行外部合约调用(即 f 为某个实现外部调用的内置函数
的标识符),则形成新的全局栈帧 gfrm,并在原局部栈帧 lfrm′ 中用形如 ERetId(_,_)的返回值占位符替换表达式
0
EImFunCall f es,形成局部栈帧 lfrm′′ (B→F).被调合约可正常终止于形如〈…〉 H 的全局栈帧(如图 1(G)所示),并向
0
调用者返回 1;被调合约亦可异常终止于形如〈…〉 E 的全局栈帧(如图 1(I)所示),并向调用者返回 0.
Fig.1 Changes of the semantic configuration with function calls and returns
图 1 函数调用和返回相关状态变化
以下说明函数调用和返回所涉及的关键语义规则.函数调用表达式 EFunCall f es 的第 1 步执行如下.
1. step tre ([〈(EFunCall f es,ls,fs) E #lstk′,gs,ck〉 N ])=(
2. if (f∉lm_dom builtin_ctx)∧(valid (gas gs) 3 (ct gs))
3. then (let gs′=gs (| gas:=(gas gs)−3 |)
4. in [〈(EImFunCall f es,ls,fs) E #lstk′,gs′,ck〉 N ])
5. else (if (f∈lm_dom builtin_ctx) then ([〈(EImFunCall f es,ls,fs) E #lstk′,gs,ck〉 N ]) else [〈ck〉 E ])
6. )
为了对 gas 消耗量进行描述,EFunCall f es 首先转化为中间表达式 EImFunCall f es.自定义函数调用和内置
函数调用在 gas 消耗方面存在差异.当判断该语句是自定义函数调用时,该步执行需要消耗 3 个单位的 gas 值(第
3 行);当判断该语句为内置函数的调用时,则该步执行无需消耗 gas(第 5 行 then 中的部分).
中间表达式 EImFunCall f es 的语义规则如下所示.
1. step tre [〈(EImFunCall f es,ls,fs) E #lstk′,gs,ck〉 N ]=(
2. let (idx,found)=lst_find_idx(rev es) not_lit in (
3. if found then (
4. step_ctx tre (es!(|es-idx-1|)) (ECFunCall f(take|es-idx-1| es) Hole (map peel(drop|es-idx|es)))
5. [〈(EStop,ls,fs) E #lstk′,gs,ck〉 N ]
6. ) else (
7. case lm_get((aggr_fs((EImFunCall f es,ls,fs) E #lstk′))@builtin_ctx) f of
8. Some (FunctionV f ptl rtl blk)⇒(…