正因为如此,它被设计成为这三种平台的可用的共同标准。9 Z# H* y) T$ F; T, B* I
它已经可以用于 Solidity 内部的“内联汇编”,并且未来版本的 Solidity 编译器甚至会将 Yul 用作中间语言。 为 Yul 构建高级的优化器阶段也将会很容易。" U* B7 n1 \- [* l9 d3 ?! }
… note::
请注意,用于“内联汇编”的书写风格是不带类型的(所有的都是 ``u256``),内置函数与 |evm| 操作码相同。
有关详细信息,请参阅内联汇编文档。# x+ ]& m8 o' \& E
Yul 的核心组件是函数,代码块,变量,字面量,for 循环,if 条件语句,switch 条件语句,表达式和变量赋值。2 c Z- K' X/ S P% c
Yul 是强类型的,变量和字面量都需要通过前缀符号来指明类型。支持的类型有:bool, u8, s8, u32, s32, Q4 }9 u1 o; i" \8 R
u64, s64, u128, s128, u256 和 s256。1 A: S$ Y. V B! }) o1 N
Yul 本身甚至不提供操作符。如果目标平台是 |evm|,则操作码将作为内置函数提供,但如果后端平台发生了变化,则可以重新实现它们。
有关强制性的内置函数的列表,请参阅下面的章节。
以下示例程序假定 |evm| 操作码 mul,div 和 mo 是原生支持或可以作为函数用以计算指数的。
… code::
{
function power(base:u256, exponent:u256) -> result:u256% R' E# c# c5 f5 ]# k2 P
{
switch exponent
case 0:u256 { result := 1:u256 }2 Y+ @8 u' P, x. m: q5 O
case 1:u256 { result := base }" t! Z/ l# ]" i+ R/ |
default:! a. w. P- L* }* m5 E7 J
{
result := power(mul(base, base), div(exponent, 2:u256))2 T+ h! N6 r! z0 f
switch mod(exponent, 2:u256)
case 1:u256 { result := mul(base, result) }2 m7 A! a# T( {. K* U) ?
}) i# w* g1 v7 R
}- A1 A* } M+ T$ B% I4 V% s3 G
}( N2 k; N1 ~. [, Q: ]. ?5 }: C
也可用 for 循环代替递归来实现相同的功能。这里,我们需要 |evm| 操作码 lt (小于)和 add 可用。
… code::
{
function power(base:u256, exponent:u256) -> result:u256! _! T5 p0 M/ t* p, M8 m. s
{
result := 1:u256$ T: n$ [; Q, h5 n
for { let i := 0:u256 } lt(i, exponent) { i := add(i, 1:u256) }
{1 a/ G/ y% C) j- x% M9 ]
result := mul(result, base)
}7 D2 L4 r' j8 @* Q( e; J
}
}
Yul 语言说明
本章介绍 Yul 代码。Yul 代码通常放置在一个 Yul 对象中,它将在下一节中介绍。
语法::
代码块 = '{' 语句* '}'" ~ C' ~$ s( S0 f
语句 =
代码块 |& ?: a$ G: q# v8 y3 S" ^, u; f
函数定义 |# G, y" j: ?9 G2 U1 @) B8 l9 X
变量声明 |! ^. f! N2 \ w7 r7 ?3 k
赋值 |
表达式 |
Switch |
For 循环 |
循环中断5 o# ]# i, O) s/ j6 K1 y
函数定义 =
'function' 标识符 '(' 带类型的标识符列表? ')'$ o/ a6 @3 T; S/ o
( '->' 带类型的标识符列表 )? 代码块
变量声明 =: Y* C n( ], `5 ~+ m
'let' 带类型的标识符列表 ( ':=' 表达式 )?: W) p5 ]+ c F! c. f; J
赋值 =
标识符列表 ':=' 表达式# v$ q/ D0 q! n9 v
表达式 =. J8 f" h& l0 {4 R
函数调用 | 标识符 | 字面量
If 条件语句 =
'if' 表达式 代码块
Switch 条件语句 =& k) O% j7 t/ i2 c
'switch' 表达式 Case* ( 'default' 代码块 )?$ W! ^4 e, I" n, f4 J; q0 @7 m
Case =3 u! h4 U) H) z7 C0 N
'case' 字面量 代码块
For 循环 =
'for' 代码块 表达式 代码块 代码块
循环中断 =6 B) Z! F% p& \+ j+ U! ~
'break' | 'continue'8 Z8 ]5 h# A: r, U( c" z
函数调用 =% a9 O" H# q: a' @
标识符 '(' ( 表达式 ( ',' 表达式 )* )? ')'6 ^ `7 L3 ?/ o; S4 I. U
标识符 = [a-zA-Z_$] [a-zA-Z_0-9]*: O! ]" s. N; L* ]
标识符列表 = 标识符 ( ',' 标识符)*% ^" H Q0 ]5 T5 U7 i
类型名 = 标识符 | 内置的类型名
内置的类型名 = 'bool' | [us] ( '8' | '32' | '64' | '128' | '256' )
带类型的标识符列表 = 标识符 ':' 类型名 ( ',' 标识符 ':' 类型名 )*
字面量 =
(数字字面量 | 字符串字面量 | 十六进制字面量 | True字面量 | False字面量) ':' 类型名% Z4 r% U0 j8 h. S e
数字字面量 = 十六进制数字 | 十进制数字, G7 F' g7 Z/ y, s# e
十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'# m- T- Z. }$ ?% S7 H
True字面量 = 'true'3 e$ N# N. H6 [ ?0 c& g; S- c
False字面量 = 'false'/ |! z- [& k1 V* z- K0 X! \1 }
十六进制数字 = '0x' [0-9a-fA-F]++ ?# P( W( u) g7 h
十进制数字 = [0-9]+* s* D; }( ]9 ^2 T% q$ T
语法层面的限制
Switches 必须至少有一个 case(包括 default )。, v) A+ J' l; {
如果表达式的所有可能值都被覆盖了,那么不应该允许使用 default
(即带 bool 表达式的 switch 语句同时具有 true case 和 false case 的情况下不应再有 default 语句)。
每个表达式都求值为零个或多个值。 标识符和字面量求值为一个值,函数调用求值为所调用函数的返回值。
在变量声明和赋值中,右侧表达式(如果存在)求值后,必须得出与左侧变量数量相等的值。. Q' J& O0 |4 @* h1 |
这是唯一允许求值出多个值的表达式。9 G, S8 z" [& e8 A" N2 _
那种同时又是语句的表达式(即在代码块的层次)求值结果必须只有零个值。
在其他所有情况中,表达式求值后必须仅有一个值。
continue 和 break 语句只能用在循环体中,并且必须与循环处于同一个函数中(或者两者都必须在顶层)。$ R9 v$ k3 c( x- H
for 循环的条件部分的求值结果只能为一个值。
字面量不可以大于它们本身的类型。已定义的最大类型宽度为 256 比特。
作用域规则8 V5 U7 D% f7 r; t
Yul 中的作用域是与块(除了函数和 for 循环,如下所述)和所有引入新的标识符到作用域中的声明' \: C1 T# f# _/ U$ M" o2 c
( FunctionDefinition ,VariableDeclaration )紧密绑定的。
标识符在将其定义的块中可见(包括所有子节点和子块)。
作为例外,for 循环的 “init” 部分中(第一个块)定义的标识符在 for 循环的所有其他部分(但不在循环之外)中都是可见的。
在 for 循环的其他部分声明的标识符遵守常规的作用域语法规则。
函数的参数和返回参数在函数体中可见,并且它们的名称不能相同。
变量只能在声明后引用。 尤其是,变量不能在它们自己的变量声明的右边被引用。0 @0 m1 Y) ?1 i
函数可以在声明之前被引用(如果它们是可见的)。8 t# u4 g, @! ?* P" P# s
Shadowing 是不被允许的,即是说,你不能在同名标识符已经可见的情况下又定义该标识符,即使它是不可访问的。' ~7 I6 R# R$ `$ D! j
在函数内,不可能访问声明在函数外的变量。, M3 p5 h5 t" L. M+ x: g! L' m
形式规范
我们通过在 AST 的各个节点上提供重载的求值函数 E 来正式指定 Yul。
任何函数都可能有副作用,所以 E 接受两个状态对象和 AST 节点作为它的参数,并返回两个新的状态对象和数量可变的其他值。9 N4 Z. S. y5 e& z- J
这两个状态对象是全局状态对象(在 |evm| 的上下文中是 |memory|,|storage| 和区块链的状态)和本地状态对象(局部变量的状态,即 |evm| 中堆栈的某个段)。
如果 AST 节点是一个语句,E 将返回两个状态对象和一个用于 break 和 continue 语句的 “mode”。: A. }2 _, p B) u4 T
如果 AST 节点是表达式,则 E 返回两个状态对象,并返回与表达式求值结果相同数量的值。
在这份高层次的描述中,并没有对全局状态的确切本质进行说明。
本地状态 L 是标识符 i 到值 v 的映射,表示为 L = v。9 d: S7 C3 C/ |/ R' p6 ?0 i! o
对于标识符 v, 我们用 $v 作为标识符的名字。
我们将为 AST 节点使用解构符号。0 v' R# l; g+ |
… code::
E(G, L, : Block) =
let G1, L1, mode = E(G, L, St1, ..., Stn)& o0 U8 z; E% L2 u
let L2 be a restriction of L1 to the identifiers of L/ n3 i) b7 r- @6 E) h* U+ X
G1, L2, mode, X& l) t5 B+ s5 Y$ K* c
E(G, L, St1, ..., Stn: Statement) =
if n is zero:, T! i& B& |! }+ a* G. X `
G, L, regular
else:7 W' K6 P# x& B2 j* E* h
let G1, L1, mode = E(G, L, St1)
if mode is regular then7 M0 s) I' T/ O' {. }
E(G1, L1, St2, ..., Stn)3 Y( G% Z& o, I E: Z4 y9 |9 F
otherwise
G1, L1, mode1 n9 f4 s5 [4 j6 g+ B- b5 j; U
E(G, L, FunctionDefinition) =' }1 t2 I8 U3 J0 l8 x! z
G, L, regular
E(G, L, : VariableDeclaration) =
E(G, L, : Assignment)
E(G, L, : VariableDeclaration) =
let L1 be a copy of L where L1[$vari] = 0 for i = 1, ..., n/ d$ {3 R$ U" y5 b4 @' [
G, L1, regular
E(G, L, : Assignment) =) C, m2 x2 _. I) [, W' r
let G1, L1, v1, ..., vn = E(G, L, rhs)
let L2 be a copy of L1 where L2[$vari] = vi for i = 1, ..., n `# p8 _2 ^+ O; ]3 ~) ^2 {
G, L2, regular
E(G, L, : ForLoop) =
if n >= 1:$ O* @" r9 J" v' I- W+ ]
let G1, L1, mode = E(G, L, i1, ..., in)$ W4 B$ X( W6 O$ O4 h4 J
// 由于语法限制,mode 必须是规则的' n! S+ o, S* P
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:8 {7 y/ _* I N
G1, L1, regular
else:
let G2, L2, mode = E(G1, L, body)" q; D; E9 L% a1 a, O0 x
if mode is break:/ t5 U4 n; r3 R. x( w( u: v4 O$ e
G2, L2, regular$ d# ?$ j% y: M; }' ]* I
else:
G3, L3, mode = E(G2, L2, post)" \9 U7 Z. u6 j7 Y2 O" _( l
E(G3, L3, for {} condition post body)5 z% x! C) y# c
E(G, L, break: BreakContinue) =5 ~' a, y% h/ y+ T- g
G, L, break
E(G, L, continue: BreakContinue) =
G, L, continue
E(G, L, : If) =* r3 W t1 J' ^& N
let G0, L0, v = E(G, L, condition) P& ^ J9 c! E* p2 K* i
if v is true:
E(G0, L0, body)" d( q7 h# D8 ^. W3 u
else:% T! d9 {, O! e. h9 U9 p
G0, L0, regular
E(G, L, : Switch) =- @8 ?2 B$ f8 {3 N0 `7 ]6 [& h
E(G, L, switch condition case l1:t1 st1 ... case ln:tn stn default {})2 y2 x% C7 g# K' E6 c
E(G, L, : Switch) =
let G0, L0, v = E(G, L, condition)
// i = 1 .. n7 \9 Z* J# w" H! T) p; I
// 对字面量求值,上下文无关
let _, _, v1 = E(G0, L0, l1)1 N! n4 m$ f& R8 j/ i5 z
...
let _, _, vn = E(G0, L0, ln)5 A9 T. x/ h0 i6 P/ G
if there exists smallest i such that vi = v:
E(G0, L0, sti)
else:7 C( G5 a9 a. F7 [& R7 {, |5 X
E(G0, L0, st')( \2 s! e! `. z
E(G, L, : Identifier) =
G, L, L[$name]' U; o# c% N+ j1 I. `$ @1 G) }
E(G, L, : FunctionCall) =/ ^8 q5 A3 |2 H9 e, i6 C" W+ @
G1, L1, vn = E(G, L, argn)
...
G(n-1), L(n-1), v2 = E(G(n-2), L(n-2), arg2). T) T9 A4 H" [6 v
Gn, Ln, v1 = E(G(n-1), L(n-1), arg1)8 f4 c7 N9 n* q3 ]
Let ret1, ..., retm block>6 G0 N3 j0 ^$ w1 D8 y
be the function of name $fname visible at the point of the call.$ n8 [; {& U: G4 h: a. ?
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)6 j1 T% i( W$ [% @2 i
G'', Ln, L''[$ret1], ..., L''[$retm]# t# E/ L5 q/ s5 [5 I% c) w3 l
E(G, L, l: HexLiteral) = G, L, hexString(l),1 W: Q. G. E {2 |3 i. p- y
where hexString decodes l from hex and left-aligns it into 32 bytes5 s) b# v2 {8 ]4 f
E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l),
where utf8EncodeLeftAligned performs a utf8 encoding of l* I/ N& P7 [8 `. ?: z- S0 {' B( e
and aligns it left into 32 bytes
E(G, L, n: HexNumber) = G, L, hex(n)) W) T4 Z9 |# `7 ?- a* _
where hex is the hexadecimal decoding function
E(G, L, n: DecimalNumber) = G, L, dec(n),
where dec is the decimal decoding function
类型转换函数
Yul 不支持隐式类型转换,因此存在提供显式转换的函数。
在将较大类型转换为较短类型时,如果发生溢出,则可能会发生运行时异常。9 F! I& _% l; m+ d. B
下列类型的“截取式”转换是允许的:5 J2 f! X4 }% o0 T( R4 j6 \
这里的每种类型的转换函数都有一个格式为 to(x:) -> y: 的原型,
比如 u32tobool(x:u32) -> y:bool、u256tou32(x:u256) -> y:u32 或 s256tou256(x:s256) -> y:u256。8 w( v; l8 |! F8 m1 G2 N6 e) \
… note::$ I! {) T! i( _5 U
``u32tobool(x:u32) -> y:bool`` 可以由 ``y := not(iszerou256(x))`` 实现,并且
``booltou32(x:bool) -> y:u32`` 可以由 ``switch x case true:bool { y := 1:u32 } case false:bool { y := 0:u32 }`` 实现
低级函数: j/ C( F4 V9 G5 z
以下函数必须可用:
±--------------------------------------------------------------------------------------------------------------+2 s" z. X7 C! B& `$ E- O
| 逻辑操作 |
±--------------------------------------------±----------------------------------------------------------------++ e: H( K/ L# L& {) @% E1 j
| not(x:bool) -> z:bool | 逻辑非 |* J4 N/ Z9 a; }1 w
±--------------------------------------------±----------------------------------------------------------------+
| and(x:bool, y:bool) -> z:bool | 逻辑与 |$ F [) O# V; D6 O" r
±--------------------------------------------±----------------------------------------------------------------+& M$ s% y. S2 m J+ E! p
| or(x:bool, y:bool) -> z:bool | 逻辑或 |
±--------------------------------------------±----------------------------------------------------------------+
| xor(x:bool, y:bool) -> z:bool | 异或 |
±--------------------------------------------±----------------------------------------------------------------+
| 算术操作 |4 F5 R0 f/ [7 q, \2 C6 R5 E
±--------------------------------------------±----------------------------------------------------------------+; ?* _. _$ P$ e' w8 Q/ ?
| addu256(x:u256, y:u256) -> z:u256 | x + y |
±--------------------------------------------±----------------------------------------------------------------+/ H5 C0 Q# I- P/ r7 \6 M8 C
| subu256(x:u256, y:u256) -> z:u256 | x - y | A) I& r/ @& I* d) R5 |
±--------------------------------------------±----------------------------------------------------------------+. t# b, t R5 J8 Q) ^4 C% p# \
| mulu256(x:u256, y:u256) -> z:u256 | x * y |8 C+ h( K( z& S7 n, z7 k
±--------------------------------------------±----------------------------------------------------------------+3 H, S( u( j/ P/ w+ l+ V$ T
| divu256(x:u256, y:u256) -> z:u256 | x / y |. }) ^! h' V1 M5 t# F' F
±--------------------------------------------±----------------------------------------------------------------+
| divs256(x:s256, y:s256) -> z:s256 | x / y, 有符号数用补码形式 |, k! y- M5 |% F8 d, h& J1 l! {
±--------------------------------------------±----------------------------------------------------------------+
| modu256(x:u256, y:u256) -> z:u256 | x % y |9 p, V* K2 |" y, X' }) a% z" A6 d, s
±--------------------------------------------±----------------------------------------------------------------+
| mods256(x:s256, y:s256) -> z:s256 | x % y, 有符号数用补码形式 |
±--------------------------------------------±----------------------------------------------------------------+& Y9 G/ k5 T& w- S/ R$ v
| signextendu256(i:u256, x:u256) -> z:u256 | 从第 (i*8+7) 位开始进行符号扩展,从最低符号位开始计算 |
±--------------------------------------------±----------------------------------------------------------------+
| expu256(x:u256, y:u256) -> z:u256 | x 的 y 次方 |# g$ M# v! p5 M2 n( `
±--------------------------------------------±----------------------------------------------------------------++ E2 u4 i8 R/ D8 i0 n) ?" |
| addmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x + y) % m |
±--------------------------------------------±----------------------------------------------------------------+- }/ B: y: ]- J- n" i
| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x * y) % m |* u* f/ {* f" B( K
±--------------------------------------------±----------------------------------------------------------------+6 n5 o5 g Q7 M
| ltu256(x:u256, y:u256) -> z:bool | 若 x z:bool | 若 x > y 为 true, 否则为 false |$ N8 N/ x3 L9 H5 ]; P! L
±--------------------------------------------±----------------------------------------------------------------+
| sltu256(x:s256, y:s256) -> z:bool | 若 x z:bool | 若 x > y 为 true, 否则为 false |
| | 有符号数用补码形式 |
±--------------------------------------------±----------------------------------------------------------------+1 M# w! Y% X0 j/ }9 r$ |
| equ256(x:u256, y:u256) -> z:bool | 若 x == y 为 true, 否则为 false |
±--------------------------------------------±----------------------------------------------------------------+
| iszerou256(x:u256) -> z:bool | 若 x == 0 为 true, 否则为 false |& D U2 P& t9 R& @. L5 H8 J! Y
±--------------------------------------------±----------------------------------------------------------------+
| notu256(x:u256) -> z:u256 | ~x, 对 x 按位非 |
±--------------------------------------------±----------------------------------------------------------------+
| andu256(x:u256, y:u256) -> z:u256 | x 和 y 按位与 |. b, e0 f) x& W' J
±--------------------------------------------±----------------------------------------------------------------+
| oru256(x:u256, y:u256) -> z:u256 | x 和 y 按位或 |
±--------------------------------------------±----------------------------------------------------------------+
| xoru256(x:u256, y:u256) -> z:u256 | x 和 y 按位异或 |
±--------------------------------------------±----------------------------------------------------------------+
| shlu256(x:u256, y:u256) -> z:u256 | 将 x 逻辑左移 y 位 |
±--------------------------------------------±----------------------------------------------------------------+
| shru256(x:u256, y:u256) -> z:u256 | 将 x 逻辑右移 y 位 |
±--------------------------------------------±----------------------------------------------------------------+
| saru256(x:u256, y:u256) -> z:u256 | 将 x 算术右移 y 位 |( s" _0 y+ m# x \9 q _6 Y
±--------------------------------------------±----------------------------------------------------------------+
| byte(n:u256, x:u256) -> v:u256 | x 的第 n 字节,这里的索引位置是从 0 开始的; |. ^& d: c L& Y! w6 Q1 {
| | 能否用 and256(shr256(n, x), 0xff) 来替换它, |
| | 并使它在 EVM 后端之外被优化呢? |
±--------------------------------------------±----------------------------------------------------------------+
| 内存和存储 |' {- d q" q8 J) s3 t
±--------------------------------------------±----------------------------------------------------------------+
| mload(p:u256) -> v:u256 | mem[p…(p+32)) |
±--------------------------------------------±----------------------------------------------------------------+
| mstore(p:u256, v:u256) | mem[p…(p+32)) := v |; |9 p6 I/ Y# @4 B5 V
±--------------------------------------------±----------------------------------------------------------------+
| mstore8(p:u256, v:u256) | mem := v & 0xff - 仅修改单个字节 |
±--------------------------------------------±----------------------------------------------------------------+. h# n2 \$ D& I( P9 D& z
| sload(p:u256) -> v:u256 | storage |
±--------------------------------------------±----------------------------------------------------------------+
| sstore(p:u256, v:u256) | storage := v |
±--------------------------------------------±----------------------------------------------------------------+
| msize() -> size:u256 | 内存的大小, 即已访问过的内存的最大下标, |8 t0 S5 J: o. c' `5 p
| | 因为内存扩展的限制(只能按字进行扩展) |5 ~) T5 O% U, `$ _! V
| | 返回值永远都是 32 字节的倍数 |8 w% S n; y" I0 Q; O: E
±--------------------------------------------±----------------------------------------------------------------+
| 执行控制 |
±--------------------------------------------±----------------------------------------------------------------+) W, P4 X0 O3 U# o- K7 _( \* Q- B
| create(v:u256, p:u256, s:u256) | 以 mem[p…(p+s)) 上的代码创建一个新合约,发送 |8 ^) W( |/ h: ~9 W
| | v 个 wei,并返回一个新的地址 |
±--------------------------------------------±----------------------------------------------------------------+
| call(g:u256, a:u256, v:u256, in:u256, | 调用地址 a 上的合约,以 mem[in…(in+insize)) 作为输入 |8 e8 \2 X( U& S- j
| insize:u256, out:u256, | 一并发送 g gas 和 v wei ,以 mem[out…(out+outsize)) |! q! A) _5 x! `) w
| outsize:u256) | 作为输出空间。若错误,返回 0 (比如,gas 用光 |5 E: h! H, L$ ~% O- m5 A2 m
| -> r:u256 | 成功,返回 1 |9 M' V; M3 K4 p0 q$ Z1 l0 I$ L+ c3 J f. b
±--------------------------------------------±----------------------------------------------------------------+
| callcode(g:u256, a:u256, v:u256, in:u256, | 相当于 call 但仅仅使用地址 a 上的代码, |
| insize:u256, out:u256, | 而留在当前合约的上下文当中 |
| outsize:u256) -> r:u256 | |
±--------------------------------------------±----------------------------------------------------------------+( c1 Q% Z3 \/ ?0 R2 n( ?9 a" m: d
| delegatecall(g:u256, a:u256, in:u256, | 相当于 callcode, |" s# J- l& o9 O o- w0 K9 u
| insize:u256, out:u256, | 但同时保留 caller |# Z) a6 T3 B3 {1 ^6 A
| outsize:u256) -> r:u256 | 和 callvalue |5 e& D: N9 o$ l# I ]) p! d
±--------------------------------------------±----------------------------------------------------------------+8 |$ C; a; B2 V( L7 b* s* w
| abort() | 终止 (相当于EVM上的非法指令) |
±--------------------------------------------±----------------------------------------------------------------+
| return(p:u256, s:u256) | 终止执行,返回 mem[p…(p+s)) 上的数据 |* _: F3 D. J* n5 w% Y) L; t
±--------------------------------------------±----------------------------------------------------------------+% J7 g/ R) ~6 B- M. x
| revert(p:u256, s:u256) | 终止执行,恢复状态变更,返回 mem[p…(p+s)) 上的数据 |
±--------------------------------------------±----------------------------------------------------------------+
| selfdestruct(a:u256) | 终止执行,销毁当前合约,并且将余额发送到地址 a |
±--------------------------------------------±----------------------------------------------------------------+ [" }9 g) `- P% i# B
| log0(p:u256, s:u256) | 用 mem[p…(p+s)] 上的数据产生日志,但没有 topic |
±--------------------------------------------±----------------------------------------------------------------+3 Q/ [# z9 {. a; I$ z# ?
| log1(p:u256, s:u256, t1:u256) | 用 mem[p…(p+s)] 上的数据和 topic t1 产生日志 |7 M* h2 {+ }! u) F
±--------------------------------------------±----------------------------------------------------------------+
| log2(p:u256, s:u256, t1:u256, t2:u256) | 用 mem[p…(p+s)] 上的数据和 topic t1,t2 产生日志 |2 _/ b r$ _7 r6 G! c) `; X
±--------------------------------------------±----------------------------------------------------------------+
| log3(p:u256, s:u256, t1:u256, t2:u256, | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3 产生日志 |
| t3:u256) | |- m' i+ Q) {4 N. [" [# x& | I
±--------------------------------------------±----------------------------------------------------------------+! t# }; \ e6 i! m% ~+ d
| log4(p:u256, s:u256, t1:u256, t2:u256, | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3,t4 |3 v: F0 X$ U' _. r9 M5 m0 ], {6 F3 V! s
| t3:u256, t4:u256) | 产生日志 |2 d/ T4 z8 _8 ^3 D8 e, f: @$ D
±--------------------------------------------±----------------------------------------------------------------+1 ]2 p, K) \. O3 B6 c+ q7 }2 ~: z
| 状态查询 |
±--------------------------------------------±----------------------------------------------------------------+% j% \* e' D) M O6 {2 [! F0 R F
| blockcoinbase() -> address:u256 | 当前的矿工 |5 p% k( v5 b' u+ M" D: d6 s Q
±--------------------------------------------±----------------------------------------------------------------+# i( ~5 E# E; v$ K+ q/ L0 y' ?
| blockdifficulty() -> difficulty:u256 | 当前区块的难度 |
±--------------------------------------------±----------------------------------------------------------------+
| blockgaslimit() -> limit:u256 | 当前区块的区块 gas 限制 |* ~* I9 H9 j L
±--------------------------------------------±----------------------------------------------------------------+
| blockhash(b:u256) -> hash:u256 | 区块号为 b 的区块的哈希, |
| | 仅可用于最近的 256 个区块,不包含当前区块 |
±--------------------------------------------±----------------------------------------------------------------+7 w4 V2 C! m% N9 l. p& G
| blocknumber() -> block:u256 | 当前区块号 |
±--------------------------------------------±----------------------------------------------------------------+
| blocktimestamp() -> timestamp:u256 | 自 epoch 开始的,当前块的时间戳,以秒为单位 |
±--------------------------------------------±----------------------------------------------------------------+6 B( s6 O) C1 j/ x! o$ d
| txorigin() -> address:u256 | 交易的发送方 |( W3 q: U$ p' s
±--------------------------------------------±----------------------------------------------------------------+; @" G0 I! F# y; g) p
| txgasprice() -> price:u256 | 交易中的 gas 价格 |
±--------------------------------------------±----------------------------------------------------------------+
| gasleft() -> gas:u256 | 还可用于执行的 gas |! }$ v- j# r, X% h6 O D
±--------------------------------------------±----------------------------------------------------------------+8 [3 `' g* m7 B& t, v1 \ V
| balance(a:u256) -> v:u256 | 地址 a 上的 wei 余额 |! X6 D9 p4 t, z, Q6 [3 I
±--------------------------------------------±----------------------------------------------------------------+" `2 y, l1 R8 ` A! N
| this() -> address:u256 | 当前合约/执行上下文的地址 |
±--------------------------------------------±----------------------------------------------------------------+- [' t' ^7 s2 h
| caller() -> address:u256 | 调用的发送方 (不包含委托调用) |
±--------------------------------------------±----------------------------------------------------------------+
| callvalue() -> v:u256 | 与当前调用一起发送的 wei |
±--------------------------------------------±----------------------------------------------------------------+4 }2 b" `7 u1 b( U
| calldataload(p:u256) -> v:u256 | 从 position p 开始的 calldata (32 字节) |6 N- S- g a% h( Y+ \
±--------------------------------------------±----------------------------------------------------------------+8 p) Q8 I2 d% `* b* s! K/ X
| calldatasize() -> v:u256 | 以字节为单位的 calldata 的大小 |$ A7 G+ k/ B5 Y
±--------------------------------------------±----------------------------------------------------------------+1 {. T+ W* P& H& l3 L" s$ z
| calldatacopy(t:u256, f:u256, s:u256) | 从位置为 f 的 calldata 中,拷贝 s 字节到内存位置 t |
±--------------------------------------------±----------------------------------------------------------------+# q0 _3 J+ e7 `( I; f) Q8 L, t, \% _
| codesize() -> size:u256 | 当前合约/执行上下文的代码大小 |
±--------------------------------------------±----------------------------------------------------------------+
| codecopy(t:u256, f:u256, s:u256) | 从 code 位置 f 拷贝 s 字节到内存位置 t |9 f) B. E5 B/ c' W0 g; _/ x5 y4 ^8 O
±--------------------------------------------±----------------------------------------------------------------+3 F! \# G }. P4 h' {! a
| extcodesize(a:u256) -> size:u256 | 地址 a 上的代码大小 |8 |$ J" e) ^0 I; s' k* ^1 g
±--------------------------------------------±----------------------------------------------------------------+
| extcodecopy(a:u256, t:u256, f:u256, s:u256) | 相当于 codecopy(t, f, s),但从地址 a 获取代码 |" A0 m: @1 Z8 M8 s. M' C
±--------------------------------------------±----------------------------------------------------------------+
| 其他 |8 s$ e( |1 S' G; G) [3 G
±--------------------------------------------±----------------------------------------------------------------+
| discard(unused:bool) | 丢弃值 |- M3 m C o* u7 M/ N/ u u# [: [* O: u
±--------------------------------------------±----------------------------------------------------------------+
| discardu256(unused:u256) | 丢弃值 |2 ?: ]0 `. U# Y' R' ?8 Q# e* G
±--------------------------------------------±----------------------------------------------------------------+/ e) I6 x1 f+ `2 ~% Q, N
| splitu256tou64(x:u256) -> (x1:u64, x2:u64, | 将一个 u256 拆分为四个 u64 |
| x3:u64, x4:u64) | |
±--------------------------------------------±----------------------------------------------------------------+) y: Q. `1 S& N( d
| combineu64tou256(x1:u64, x2:u64, x3:u64, | 将四个 u64 组合为一个 u256 |
| x4:u64) -> (x:u256) | |- v7 L1 L4 u1 s! m" C
±--------------------------------------------±----------------------------------------------------------------+& p* s7 Q: r. b L& F2 ^
| keccak256(p:u256, s:u256) -> v:u256 | keccak(mem[p…(p+s))) |
±--------------------------------------------±----------------------------------------------------------------+
后端+ h. L1 q3 I% t6 h5 J1 d
后端或目标负责将 Yul 翻译到特定字节码。 每个后端都可以暴露以后端名称为前缀的函数。 我们为两个建议的后端保留 evm_ 和 ewasm_ 前缀。& T. Z+ |6 O3 b4 I
后端: EVM, p, n3 v3 m/ U1 A7 A1 }5 r
目标 |evm| 将具有所有用 evm_ 前缀暴露的 |evm| 底层操作码。& l- H2 G$ r" S1 m0 K% J' h7 A
后端: “EVM 1.5”
TBD
后端: eWASM
TBD
Yul 对象说明/ @) m& {/ M% h6 c% M9 s
语法::
顶层对象 = 'object' '{' 代码? ( 对象 | 数据 )* '}'6 B# o7 H2 L' g# Y$ k J& r8 X
对象 = 'object' 字符串字面量 '{' 代码? ( 对象 | 数据 )* '}'
代码 = 'code' 代码块$ P8 I" b& R% B- [# v8 }. [2 V+ h
数据 = 'data' 字符串字面量 十六进制字面量
十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'
在上面,代码块 指的是前一章中解释的 Yul 代码语法中的 代码块。
Yul 对象示例如下:
…code::
// 代码由单个对象组成。 单个 “code” 节点是对象的代码。 f( @! H1 j7 W. w( Z1 A
// 每个(其他)命名的对象或数据部分都被序列化
// 并可供特殊内置函数:datacopy / dataoffset / datasize 用于访问5 B* @6 ^$ Y+ \
object {/ b' ]$ ?; o: s3 X* X" u, a) t
code {
let size = datasize("runtime")
let offset = allocate(size)
// 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy
datacopy(dataoffset("runtime"), offset, size)
// 这是一个构造函数,并且运行时代码会被返回- E/ ?* S. ^& b% c1 o( K
return(offset, size)2 \4 s0 O4 N5 g; P8 `
}
data "Table2" hex"4123"3 ~7 ~' G$ S0 C5 J: P/ f
object "runtime" {9 c. s( u; P" z9 R& {6 X
code {
// 运行时代码
let size = datasize("Contract2"), M* Q5 d) w6 P+ j! ?6 R0 v4 K
let offset = allocate(size)( g1 E! ?; H; c: s4 p( w' `+ v! i3 y
// 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy0 v6 L7 R& \! c4 y: _, S
datacopy(dataoffset("Contract2"), offset, size)& B, Q# X) \$ p- f& h
// 构造函数参数是一个数字 0x12345 J- U$ m* c) R/ O4 d4 Z
mstore(add(offset, size), 0x1234)7 B& h( N' z! z1 h. A, }" r# ]
create(offset, add(size, 32))
}
// 内嵌对象。使用场景是,外层是一个工厂合约,而 Contract2 将是由工厂生成的代码( F; B5 E& Y" K) R$ A( Z7 [- Q
object "Contract2" {- `# O8 v, H" O$ s9 B; g g! F
code {6 g4 V$ _" _+ G1 J& k
// 代码在这 ...
}
object "runtime" {4 z m' S, @% ^7 k; f& ]% t
code {
// 代码在这 ...7 x9 H" a$ y- Y1 h
}
}
data "Table1" hex"4123"
}
}: l4 j. |9 z) P, M6 L1 k/ Q9 H
}