寫在前面:HiBlock區塊鏈社群成立了翻譯小組,翻譯區塊鏈相關的技術文件及資料,本文為Solidity文件翻譯的第九部分《Yul語言及物件說明》,特釋出出來邀請solidity愛好者、開發者做公開的審校,您可以新增微信baobaotalk_com,驗證輸入“solidity”,然後將您的意見和建議傳送給我們,也可以在文末“留言”區留言,有效的建議我們會採納及合併進下一版本,同時將送一份小禮物給您以示感謝。
Yul (先前被也被稱為 JULIA 或 IULIA)是一種可以編譯到各種不同後端的中間語言( 以太坊虛擬機器Ethereum Virtual Machine(EVM) 1.0,以太坊虛擬機器Ethereum Virtual Machine(EVM) 1.5,而 eWASM 也在計劃中)。 正因為如此,它被設計成為這三種平臺的可用的共同標準。 它已經可以用於 Solidity 內部的“內聯彙編”,並且未來版本的 Solidity 編譯器甚至會將 Yul 用作中間語言。 為 Yul 構建高階的優化器階段也將會很容易。
Yul 的核心元件是函式,程式碼塊,變數,字面量,for 迴圈,if 條件語句,switch 條件語句,表示式和變數賦值。
Yul 是強型別的,變數和字面量都需要通過字首符號來指明型別。支援的型別有:bool, u8, s8, u32, s32, u64, s64, u128, s128, u256 和 s256。
Yul 本身甚至不提供操作符。如果目標平臺是 以太坊虛擬機器Ethereum Virtual Machine(EVM),則操作碼將作為內建函式提供,但如果後端平臺發生了變化,則可以重新實現它們。 有關強制性的內建函式的列表,請參閱下面的章節。
以下示例程式假定 以太坊虛擬機器Ethereum Virtual Machine(EVM) 操作碼 mul,div 和 mo 是原生支援或可以作為函式用以計算指數的。
{
function power(base:u256, exponent:u256) -> result:u256
{
switch exponent
case 0:u256 { result := 1:u256 }
case 1:u256 { result := base }
default:
{
result := power(mul(base, base), div(exponent, 2:u256))
switch mod(exponent, 2:u256)
case 1:u256 { result := mul(base, result) }
}
}
}
也可用 for 迴圈代替遞迴來實現相同的功能。這裡,我們需要 以太坊虛擬機器Ethereum Virtual Machine(EVM) 操作碼 lt(小於)和 add 可用。
{
function power(base:u256, exponent:u256) -> result:u256
{
result := 1:u256
for { let i := 0:u256 } lt(i, exponent) { i := add(i, 1:u256) }
{
result := mul(result, base)
}
}
}
複製程式碼
1
Yul語言說明
本章介紹 Yul 程式碼。Yul 程式碼通常放置在一個 Yul 物件中,它將在下一節中介紹。
語法:
程式碼塊 = '{' 語句* '}'
語句 =
程式碼塊 |
函式定義 |
變數宣告 |
賦值 |
表示式 |
Switch |
For 迴圈 |
迴圈中斷
函式定義 =
'function' 識別符號 '(' 帶型別的識別符號列表? ')'
( '->' 帶型別的識別符號列表 )? 程式碼塊
變數宣告 =
'let' 帶型別的識別符號列表 ( ':=' 表示式 )?
賦值 =
識別符號列表 ':=' 表示式
表示式 =
函式呼叫 | 識別符號 | 字面量
If 條件語句 =
'if' 表示式 程式碼塊
Switch 條件語句 =
'switch' 表示式 Case* ( 'default' 程式碼塊 )?
Case =
'case' 字面量 程式碼塊
For 迴圈 =
'for' 程式碼塊 表示式 程式碼塊 程式碼塊
迴圈中斷 =
'break' | 'continue'
函式呼叫 =
識別符號 '(' ( 表示式 ( ',' 表示式 )* )? ')'
識別符號 = [a-zA-Z_$] [a-zA-Z_0-9]*
識別符號列表 = 識別符號 ( ',' 識別符號)*
型別名 = 識別符號 | 內建的型別名
內建的型別名 = 'bool' | [us] ( '8' | '32' | '64' | '128' | '256' )
帶型別的識別符號列表 = 識別符號 ':' 型別名 ( ',' 識別符號 ':' 型別名 )*
字面量 =
(數字字面量 | 字串字面量 | 十六進位制字面量 | True字面量 | False字面量) ':' 型別名
數字字面量 = 十六進位制數字 | 十進位制數字
十六進位制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')字串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'
True字面量 = 'true'
False字面量 = 'false'
十六進位制數字 = '0x' [0-9a-fA-F]+
十進位制數字 = [0-9]+
複製程式碼
語法層面的限制
Switches 必須至少有一個 case(包括 default )。 如果表示式的所有可能值都被覆蓋了,那麼不應該允許使用 default (即帶 bool 表示式的 switch 語句同時具有 true case 和 false case 的情況下不應再有 default 語句)。
每個表示式都求值為零個或多個值。 識別符號和字面量求值為一個值,函式呼叫求值為所呼叫函式的返回值。
在變數宣告和賦值中,右側表示式(如果存在)求值後,必須得出與左側變數數量相等的值。 這是唯一允許求值出多個值的表示式。
那種同時又是語句的表示式(即在程式碼塊的層次)求值結果必須只有零個值。
在其他所有情況中,表示式求值後必須僅有一個值。
continue 和 break 語句只能用在迴圈體中,並且必須與迴圈處於同一個函式中(或者兩者都必須在頂層)。
for 迴圈的條件部分的求值結果只能為一個值。
字面量不可以大於它們本身的型別。已定義的最大型別寬度為 256 位元。
作用域規則
Yul 中的作用域是與塊(除了函式和 for 迴圈,如下所述)和所有引入新的識別符號到作用域中的宣告 ( FunctionDefinition ,VariableDeclaration )緊密繫結的。
識別符號在將其定義的塊中可見(包括所有子節點和子塊)。 作為例外,for 迴圈的 “init” 部分中(第一個塊)定義的識別符號在 for 迴圈的所有其他部分(但不在迴圈之外)中都是可見的。 在 for 迴圈的其他部分宣告的識別符號遵守常規的作用域語法規則。 函式的引數和返回引數在函式體中可見,並且它們的名稱不能相同。
變數只能在宣告後引用。 尤其是,變數不能在它們自己的變數宣告的右邊被引用。 函式可以在宣告之前被引用(如果它們是可見的)。
Shadowing 是不被允許的,即是說,你不能在同名識別符號已經可見的情況下又定義該識別符號,即使它是不可訪問的。
在函式內,不可能訪問宣告在函式外的變數。
形式規範
我們通過在 AST 的各個節點上提供過載的求值函式 E 來正式指定 Yul。 任何函式都可能有副作用,所以 E 接受兩個狀態物件和 AST 節點作為它的引數,並返回兩個新的狀態物件和數量可變的其他值。
這兩個狀態物件是全域性狀態物件(在 以太坊虛擬機器Ethereum Virtual Machine(EVM) 的上下文中是 記憶體memory,儲存storage 和區塊鏈的狀態)和本地狀態物件(區域性變數的狀態,即 以太坊虛擬機器Ethereum Virtual Machine(EVM) 中堆疊的某個段)。 如果 AST 節點是一個語句,E 將返回兩個狀態物件和一個用於 break 和 continue 語句的 “mode”。 如果 AST 節點是表示式,則 E 返回兩個狀態物件,並返回與表示式求值結果相同數量的值。
在這份高層次的描述中,並沒有對全域性狀態的確切本質進行說明。 本地狀態 L 是識別符號 i 到值 v 的對映,表示為 L[i] = v。 對於識別符號 v, 我們用 $v 作為識別符號的名字。
我們將為 AST 節點使用解構符號。
E(G, L, <{St1, ..., Stn}>: Block) =
let G1, L1, mode = E(G, L, St1, ..., Stn)
let L2 be a restriction of L1 to the identifiers of L
G1, L2, modeE(G, L, St1, ..., Stn: Statement) =
if n is zero:
G, L, regular
else:
let G1, L1, mode = E(G, L, St1)
if mode is regular then
E(G1, L1, St2, ..., Stn)
otherwise
G1, L1, modeE(G, L, FunctionDefinition) =
G, L, regularE(G, L, <let var1, ..., varn := rhs>: VariableDeclaration) =
E(G, L, <var1, ..., varn := rhs>: Assignment)E(G, L, <let var1, ..., varn>: VariableDeclaration) =
let L1 be a copy of L where L1[$vari] = 0 for i = 1, ..., n
G, L1, regularE(G, L, <var1, ..., varn := rhs>: Assignment) =
let G1, L1, v1, ..., vn = E(G, L, rhs)
let L2 be a copy of L1 where L2[$vari] = vi for i = 1, ..., n
G, L2, regularE(G, L, <for { i1, ..., in } condition post body>: ForLoop) =
if n >= 1:
let G1, L1, mode = E(G, L, i1, ..., in)
// 由於語法限制,mode 必須是規則的
let G2, L2, mode = E(G1, L1, for {} condition post body)
// 由於語法限制,mode 必須是規則的
let L3 be the restriction of L2 to only variables of L
G2, L3, regular
else:
let G1, L1, v = E(G, L, condition)
if v is false:
G1, L1, regular
else:
let G2, L2, mode = E(G1, L, body)
if mode is break:
G2, L2, regular
else:
G3, L3, mode = E(G2, L2, post)
E(G3, L3, for {} condition post body)E(G, L, break: BreakContinue) =
G, L, breakE(G, L, continue: BreakContinue) =
G, L, continueE(G, L, <if condition body>: If) =
let G0, L0, v = E(G, L, condition)
if v is true:
E(G0, L0, body)
else:
G0, L0, regularE(G, L, <switch condition case l1:t1 st1 ... case ln:tn stn>: Switch) =
E(G, L, switch condition case l1:t1 st1 ... case ln:tn stn default {})E(G, L, <switch condition case l1:t1 st1 ... case ln:tn stn default st'>: Switch) = let G0, L0, v = E(G, L, condition) // i = 1 .. n // 對字面量求值,上下文無關 let _, _, v1 = E(G0, L0, l1) ... let _, _, vn = E(G0, L0, ln) if there exists smallest i such that vi = v: E(G0, L0, sti) else: E(G0, L0, st')E(G, L, <name>: Identifier) =
G, L, L[$name]E(G, L, <fname(arg1, ..., argn)>: FunctionCall) =
G1, L1, vn = E(G, L, argn)
...
G(n-1), L(n-1), v2 = E(G(n-2), L(n-2), arg2)
Gn, Ln, v1 = E(G(n-1), L(n-1), arg1)
Let <function fname (param1, ..., paramn) -> ret1, ..., retm block>
be the function of name $fname visible at the point of the call.
Let L' be a new local state such that L'[$parami] = vi and L'[$reti] = 0 for all i. Let G'', L'', mode = E(Gn, L', block)
G'', Ln, L''[$ret1], ..., L''[$retm]E(G, L, l: HexLiteral) = G, L, hexString(l),
where hexString decodes l from hex and left-aligns it into 32 bytesE(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l),
where utf8EncodeLeftAligned performs a utf8 encoding of l
and aligns it left into 32 bytesE(G, L, n: HexNumber) = G, L, hex(n)
where hex is the hexadecimal decoding functionE(G, L, n: DecimalNumber) = G, L, dec(n),
where dec is the decimal decoding function
複製程式碼
型別轉換函式
Yul 不支援隱式型別轉換,因此存在提供顯式轉換的函式。 在將較大型別轉換為較短型別時,如果發生溢位,則可能會發生執行時異常。
下列型別的“擷取式”轉換是允許的:
-
bool
-
u32
-
u64
-
u256
-
s256
低階函式
以下函式必須可用:
後端
後端或目標負責將 Yul 翻譯到特定位元組碼。 每個後端都可以暴露以後端名稱為字首的函式。 我們為兩個建議的後端保留 evm_ 和 ewasm_ 字首。
後端: EVM
目標 以太坊虛擬機器Ethereum Virtual Machine(EVM) 將具有所有用 evm_ 字首暴露的 以太坊虛擬機器Ethereum Virtual Machine(EVM) 底層操作碼。
後端: "EVM 1.5"
TBD
後端: eWASM
TBD
2
Yul物件說明
語法:
頂層物件 = 'object' '{' 程式碼? ( 物件 | 資料 )* '}'
物件 = 'object' 字串字面量 '{' 程式碼? ( 物件 | 資料 )* '}'
程式碼 = 'code' 程式碼塊
資料 = 'data' 字串字面量 十六進位制字面量
十六進位制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
字串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'
複製程式碼
在上面,程式碼塊 指的是前一章中解釋的 Yul 程式碼語法中的 程式碼塊。
Yul 物件示例如下:
..code:
// 程式碼由單個物件組成。 單個 “code” 節點是物件的程式碼。// 每個(其他)命名的物件或資料部分都被序列化// 並可供特殊內建函式:datacopy / dataoffset / datasize 用於訪問object {
code {
let size = datasize("runtime")
let offset = allocate(size)
// 這裡,對於 eWASM 變為一個記憶體到記憶體的拷貝,對於 EVM 則相當於 codecopy
datacopy(dataoffset("runtime"), offset, size)
// 這是一個建構函式,並且執行時程式碼會被返回
return(offset, size)
}
data "Table2" hex"4123"
object "runtime" {
code {
// 執行時程式碼
let size = datasize("Contract2")
let offset = allocate(size)
// 這裡,對於 eWASM 變為一個記憶體到記憶體的拷貝,對於 EVM 則相當於 codecopy
datacopy(dataoffset("Contract2"), offset, size)
// 建構函式引數是一個數字 0x1234
mstore(add(offset, size), 0x1234)
create(offset, add(size, 32))
}
// 內嵌物件。使用場景是,外層是一個工廠合約,而 Contract2 將是由工廠生成的程式碼
object "Contract2" {
code {
// 程式碼在這 ...
}
object "runtime" {
code {
// 程式碼在這 ...
}
}
data "Table1" hex"4123"
}
}
}
複製程式碼
延伸閱讀:智慧合約-Solidity官方文件(1)
根據例子學習Solidity-Solidity官方文件(3)
深入理解Solidity之原始檔及合約結構——Solidity中文文件(4)
應用二進位制介面(ABI) 說明——Solidity中文文件(7)
點選“閱讀原文”即可檢視完整中文文件
注:本文為solidity翻譯的第九部分****《Yul語言及物件說明》,特釋出出來邀請solidity愛好者、開發者做公開的審校,您可以新增微信baobaotalk_com,驗證輸入“solidity”,然後將您的意見和建議傳送給我們,也可在文末“留言”區留言,或通過原文連結訪問我們的Github。有效的建議我們會收納並及時改進,同時將送一份小禮物給您以示感謝。
本文內容來源於HiBlock區塊鏈社群翻譯小組,感謝全體譯者的辛苦工作。點選“閱讀原文”即可檢視完整中文文件。
線上課程推薦
線上課程:《8小時區塊鏈智慧合約開發實踐》
培訓講師:《白話區塊鏈》作者 蔣勇
課程原價:999元,現價 399元
更多福利:
-
@所有人,識別下圖二維碼轉發課程邀請好友報名,即可獲得報名費50%返利
-
@學員,報名學習課程並在規定時間內完成考試即可瓜分10000元獎金