正因为如此,它被设计成为这三种平台的可用的共同标准。
它已经可以用于 Solidity 内部的“内联汇编”,并且未来版本的 Solidity 编译器甚至会将 Yul 用作中间语言。 为 Yul 构建高级的优化器阶段也将会很容易。! G- C9 l- X( d. P
… note::3 k" Q" z" j$ B& |+ D1 p
请注意,用于“内联汇编”的书写风格是不带类型的(所有的都是 ``u256``),内置函数与 |evm| 操作码相同。( d+ h7 [; \ X
有关详细信息,请参阅内联汇编文档。* c; Q. W( Q1 x
Yul 的核心组件是函数,代码块,变量,字面量,for 循环,if 条件语句,switch 条件语句,表达式和变量赋值。
Yul 是强类型的,变量和字面量都需要通过前缀符号来指明类型。支持的类型有:bool, u8, s8, u32, s32,% e' _8 r( @6 @6 P( z
u64, s64, u128, s128, u256 和 s256。
Yul 本身甚至不提供操作符。如果目标平台是 |evm|,则操作码将作为内置函数提供,但如果后端平台发生了变化,则可以重新实现它们。3 B& T \1 O8 S8 e: g) K7 }" }8 Y
有关强制性的内置函数的列表,请参阅下面的章节。/ o5 F( g2 G* @% s
以下示例程序假定 |evm| 操作码 mul,div 和 mo 是原生支持或可以作为函数用以计算指数的。
… code::- n1 j7 V0 K c3 Y' E5 t
{. N' r6 |, c- e- p$ n1 g. K
function power(base:u256, exponent:u256) -> result:u256
{
switch exponent
case 0:u256 { result := 1:u256 }
case 1:u256 { result := base } ]/ F+ {) P3 ]
default:' b2 H( ~3 Y3 ^' B+ h
{
result := power(mul(base, base), div(exponent, 2:u256))1 M `7 c* O! \6 F0 e* \
switch mod(exponent, 2:u256)
case 1:u256 { result := mul(base, result) }1 |: s5 m* P; L# ~" {
}
}: r5 g9 \, P' m L
}
也可用 for 循环代替递归来实现相同的功能。这里,我们需要 |evm| 操作码 lt (小于)和 add 可用。' g! a- A6 {9 x7 H0 N6 {# h
… code::/ a& m7 p s: A6 T
{
function power(base:u256, exponent:u256) -> result:u256. S3 B" L% b. s8 @
{
result := 1:u256" Q! Z8 w2 n# y7 a
for { let i := 0:u256 } lt(i, exponent) { i := add(i, 1:u256) }
{
result := mul(result, base)
}
}! W8 c, g1 I6 C3 F$ F; U
}0 {: ~& |) M, k" X& ~
Yul 语言说明
本章介绍 Yul 代码。Yul 代码通常放置在一个 Yul 对象中,它将在下一节中介绍。! r) k. o: l3 Z
语法::
代码块 = '{' 语句* '}'
语句 =9 z8 r, T, f+ g9 S
代码块 |- ~1 D. z8 Y( \* O( y+ U5 F$ v
函数定义 |
变量声明 |
赋值 |9 j8 p" ~) u. _4 H" g
表达式 |
Switch |# u# j3 S! ?: B# U q
For 循环 |
循环中断5 k( [9 _( X% S0 i n! g* c; f0 s Y; I
函数定义 =( J& N' j$ [3 r4 h. r1 S& A
'function' 标识符 '(' 带类型的标识符列表? ')'
( '->' 带类型的标识符列表 )? 代码块- B5 Z; N$ `$ b. r- {
变量声明 =
'let' 带类型的标识符列表 ( ':=' 表达式 )?
赋值 =
标识符列表 ':=' 表达式2 T5 X2 Q/ q) b, G2 l: c4 H; c
表达式 =
函数调用 | 标识符 | 字面量5 Y4 z% j1 b& u' M
If 条件语句 =, D5 }* U- r6 P0 l
'if' 表达式 代码块
Switch 条件语句 =6 H- w0 ^ D/ T8 _9 D
'switch' 表达式 Case* ( 'default' 代码块 )?
Case =
'case' 字面量 代码块1 ?+ ~, G$ ?' S5 V! s3 K
For 循环 =+ a5 l. n( x6 Z- P
'for' 代码块 表达式 代码块 代码块
循环中断 =- m' t$ E# R. |" l) D( X$ B
'break' | 'continue'! {7 o3 a+ G/ w; u% u6 d% n
函数调用 =" P1 ~- E- R: O4 B
标识符 '(' ( 表达式 ( ',' 表达式 )* )? ')'& a: u9 H& Q, X* I; n
标识符 = [a-zA-Z_$] [a-zA-Z_0-9]*
标识符列表 = 标识符 ( ',' 标识符)*5 ?- @' G! x; j
类型名 = 标识符 | 内置的类型名+ l3 A. y, g) T7 i! T5 W
内置的类型名 = 'bool' | [us] ( '8' | '32' | '64' | '128' | '256' )3 ?9 ~5 `2 A$ B' Z7 L
带类型的标识符列表 = 标识符 ':' 类型名 ( ',' 标识符 ':' 类型名 )*
字面量 =6 f, b# q+ J, T r! D- e! w4 D
(数字字面量 | 字符串字面量 | 十六进制字面量 | True字面量 | False字面量) ':' 类型名" y% A' f+ J5 m! R1 f" u; |4 L
数字字面量 = 十六进制数字 | 十进制数字
十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'5 t( f/ d' i2 D* A& x) Q
True字面量 = 'true'' P! b1 t8 f; q: s2 E j3 a! x
False字面量 = 'false'9 E- D* k$ v4 e& H0 J
十六进制数字 = '0x' [0-9a-fA-F]+
十进制数字 = [0-9]+$ U; O7 m4 p R$ P
语法层面的限制& l' Q1 c% e2 z7 \! U
Switches 必须至少有一个 case(包括 default )。
如果表达式的所有可能值都被覆盖了,那么不应该允许使用 default
(即带 bool 表达式的 switch 语句同时具有 true case 和 false case 的情况下不应再有 default 语句)。
每个表达式都求值为零个或多个值。 标识符和字面量求值为一个值,函数调用求值为所调用函数的返回值。
在变量声明和赋值中,右侧表达式(如果存在)求值后,必须得出与左侧变量数量相等的值。
这是唯一允许求值出多个值的表达式。: p/ M h% `6 x4 p, T
那种同时又是语句的表达式(即在代码块的层次)求值结果必须只有零个值。
在其他所有情况中,表达式求值后必须仅有一个值。
continue 和 break 语句只能用在循环体中,并且必须与循环处于同一个函数中(或者两者都必须在顶层)。+ Z9 ~% X1 R: \2 [% s; _9 G; @
for 循环的条件部分的求值结果只能为一个值。( x' @( c- Y2 T
字面量不可以大于它们本身的类型。已定义的最大类型宽度为 256 比特。% D( [. i2 l3 h$ f' I4 k- u' {. _
作用域规则
Yul 中的作用域是与块(除了函数和 for 循环,如下所述)和所有引入新的标识符到作用域中的声明: p" z! d5 H7 D& p8 y
( FunctionDefinition ,VariableDeclaration )紧密绑定的。0 n& X) Y) S& d2 H8 n0 `
标识符在将其定义的块中可见(包括所有子节点和子块)。- f6 p) i3 K H! a; ^6 ?) `
作为例外,for 循环的 “init” 部分中(第一个块)定义的标识符在 for 循环的所有其他部分(但不在循环之外)中都是可见的。1 l4 ^8 p, b( u$ N, H- D- K( X! w
在 for 循环的其他部分声明的标识符遵守常规的作用域语法规则。& m% _3 d- }3 k+ T% e2 E/ h
函数的参数和返回参数在函数体中可见,并且它们的名称不能相同。' d; e' T$ Q' t, _& O# [
变量只能在声明后引用。 尤其是,变量不能在它们自己的变量声明的右边被引用。% t- c2 a, E1 ^; f" D8 J- n# }2 u
函数可以在声明之前被引用(如果它们是可见的)。+ h. C% w. f+ o% X1 b1 Z) m% f9 r
Shadowing 是不被允许的,即是说,你不能在同名标识符已经可见的情况下又定义该标识符,即使它是不可访问的。& m) b# B1 {( t" J
在函数内,不可能访问声明在函数外的变量。
形式规范3 D% v- z. O( G, y; l) ?
我们通过在 AST 的各个节点上提供重载的求值函数 E 来正式指定 Yul。
任何函数都可能有副作用,所以 E 接受两个状态对象和 AST 节点作为它的参数,并返回两个新的状态对象和数量可变的其他值。
这两个状态对象是全局状态对象(在 |evm| 的上下文中是 |memory|,|storage| 和区块链的状态)和本地状态对象(局部变量的状态,即 |evm| 中堆栈的某个段)。
如果 AST 节点是一个语句,E 将返回两个状态对象和一个用于 break 和 continue 语句的 “mode”。
如果 AST 节点是表达式,则 E 返回两个状态对象,并返回与表达式求值结果相同数量的值。* w/ I' O. U( o$ \6 V5 C& G
在这份高层次的描述中,并没有对全局状态的确切本质进行说明。
本地状态 L 是标识符 i 到值 v 的映射,表示为 L = v。0 R) N. Y% f+ Q, G5 K, t
对于标识符 v, 我们用 $v 作为标识符的名字。
我们将为 AST 节点使用解构符号。3 b* F' e. i; t/ _+ m; K2 v
… code::. d- @( @8 J: ^* @, |
E(G, L, : Block) =
let G1, L1, mode = E(G, L, St1, ..., Stn)0 g# `" ]+ T9 h( J2 x
let L2 be a restriction of L1 to the identifiers of L( Q+ s. A9 x4 ^4 L, H/ u8 j1 F
G1, L2, mode
E(G, L, St1, ..., Stn: Statement) =* t: B- ~6 r- O8 v8 _8 x0 n
if n is zero:2 ?" c. y5 E! k& z& \
G, L, regular5 g) q) P1 V/ @- q0 E, s+ i
else:
let G1, L1, mode = E(G, L, St1)6 V, ^5 |/ s4 r5 G# L0 I
if mode is regular then
E(G1, L1, St2, ..., Stn)
otherwise, N( u4 t. x/ m% F! v2 F* C5 |
G1, L1, mode
E(G, L, FunctionDefinition) =$ S' ]! L B$ ~# ?) @/ ^
G, L, regular
E(G, L, : VariableDeclaration) =, j: F: S; ^6 K0 ]
E(G, L, : Assignment)
E(G, L, : VariableDeclaration) =- [3 T7 H1 d5 _+ N7 u5 u
let L1 be a copy of L where L1[$vari] = 0 for i = 1, ..., n
G, L1, regular
E(G, L, : 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, regular" h! V& P! L* @4 M3 b; l
E(G, L, : ForLoop) =
if n >= 1:3 u o$ d5 G M2 `7 l0 a- Y8 q
let G1, L1, mode = E(G, L, i1, ..., in); t/ D( y8 ]5 l7 L
// 由于语法限制,mode 必须是规则的
let G2, L2, mode = E(G1, L1, for {} condition post body)
// 由于语法限制,mode 必须是规则的5 C- w/ ]) _2 k: A4 r! Z3 {
let L3 be the restriction of L2 to only variables of L- x K& a8 N) t/ O
G2, L3, regular2 ^: \! H' x: `$ s( E5 E$ Y
else:
let G1, L1, v = E(G, L, condition)! {2 w2 k& y- H8 s; o6 @
if v is false:# _) n" f! K# S
G1, L1, regular
else:
let G2, L2, mode = E(G1, L, body)
if mode is break:4 B4 d) S3 t6 D |8 M+ L
G2, L2, regular. ~$ C* H6 G- m6 P
else:8 ^. ]8 `+ [, q+ q- \5 c, w
G3, L3, mode = E(G2, L2, post)
E(G3, L3, for {} condition post body)
E(G, L, break: BreakContinue) =
G, L, break
E(G, L, continue: BreakContinue) =
G, L, continue2 ~% E' E+ d9 c0 V" V" I
E(G, L, : If) =- {5 x- z" |$ j1 M. j5 m
let G0, L0, v = E(G, L, condition)
if v is true:
E(G0, L0, body)
else:. _3 o) Z* W" M* r
G0, L0, regular
E(G, L, : Switch) =, k# J4 i% C3 n) l& {# W
E(G, L, switch condition case l1:t1 st1 ... case ln:tn stn default {})7 f, H; _8 \7 f4 R% ]% E6 p. q
E(G, L, : Switch) =
let G0, L0, v = E(G, L, condition)
// i = 1 .. n& B) ?3 q! K/ i2 h
// 对字面量求值,上下文无关4 r' @/ H9 q$ @6 D4 ]" {
let _, _, v1 = E(G0, L0, l1)* _* A$ q4 j' _, j* n
.../ H! F$ z7 h' N0 H
let _, _, vn = E(G0, L0, ln)7 }1 S: Q4 c/ C% H
if there exists smallest i such that vi = v:/ S$ o$ c" N$ Z$ G
E(G0, L0, sti)
else:* J7 R/ }* x2 r( P2 D
E(G0, L0, st')
E(G, L, : Identifier) =
G, L, L[$name]
E(G, L, : FunctionCall) =( b3 r/ V; g; ], L. C8 G
G1, L1, vn = E(G, L, argn)
...
G(n-1), L(n-1), v2 = E(G(n-2), L(n-2), arg2)/ i) Y$ P0 J9 K3 b/ u1 C( K
Gn, Ln, v1 = E(G(n-1), L(n-1), arg1)4 D2 o/ C3 q5 v- V% l. o7 a
Let ret1, ..., retm block>
be the function of name $fname visible at the point of the call.1 y. k4 \5 I }! ?
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)7 a4 g9 [7 r$ u3 ^9 Q: c: n; g
G'', Ln, L''[$ret1], ..., L''[$retm]
E(G, L, l: HexLiteral) = G, L, hexString(l),5 ]3 N2 [9 B' k) h& p
where hexString decodes l from hex and left-aligns it into 32 bytes2 X, b( T3 o, l4 L* M
E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l),
where utf8EncodeLeftAligned performs a utf8 encoding of l& _! U, B2 U) }7 ?1 n. w, R' ]
and aligns it left into 32 bytes( B! {6 J3 B$ U) g
E(G, L, n: HexNumber) = G, L, hex(n)
where hex is the hexadecimal decoding function3 P0 Q+ d1 |2 v* H8 b
E(G, L, n: DecimalNumber) = G, L, dec(n),
where dec is the decimal decoding function
类型转换函数( q. E* W9 C) Z! m* w; q6 h
Yul 不支持隐式类型转换,因此存在提供显式转换的函数。0 s1 _# V. f! s: t7 \
在将较大类型转换为较短类型时,如果发生溢出,则可能会发生运行时异常。
下列类型的“截取式”转换是允许的:. o9 I5 I# Q2 S9 F# R
这里的每种类型的转换函数都有一个格式为 to(x:) -> y: 的原型,
比如 u32tobool(x:u32) -> y:bool、u256tou32(x:u256) -> y:u32 或 s256tou256(x:s256) -> y:u256。- c' N/ D, N; J: {5 r! G$ J' o
… note::+ g6 h, g& N* g I6 C9 C9 s& B0 x
``u32tobool(x:u32) -> y:bool`` 可以由 ``y := not(iszerou256(x))`` 实现,并且! |- ]2 d9 A& H+ A$ ?. e- Z1 r8 ^
``booltou32(x:bool) -> y:u32`` 可以由 ``switch x case true:bool { y := 1:u32 } case false:bool { y := 0:u32 }`` 实现, J. s# ?# \' I, Q, K) i
低级函数6 z* o; }8 W; g Z: q/ o5 a9 o
以下函数必须可用:5 z& l! H1 T% U: N
±--------------------------------------------------------------------------------------------------------------+
| 逻辑操作 |3 S; i2 D4 a9 [% }% a; H! ]
±--------------------------------------------±----------------------------------------------------------------+% D. V6 |& L0 {0 y; j& q; u
| not(x:bool) -> z:bool | 逻辑非 |
±--------------------------------------------±----------------------------------------------------------------+- L$ ~0 s* H. W
| and(x:bool, y:bool) -> z:bool | 逻辑与 |
±--------------------------------------------±----------------------------------------------------------------+
| or(x:bool, y:bool) -> z:bool | 逻辑或 |4 k! y( o( c$ c l, K
±--------------------------------------------±----------------------------------------------------------------+* c3 H* m% O( d9 z
| xor(x:bool, y:bool) -> z:bool | 异或 |" w' @5 b% I2 v9 P: b
±--------------------------------------------±----------------------------------------------------------------+' W$ R$ H. l' D$ n; I+ a0 m9 Z
| 算术操作 |' ~" S8 Z+ f% y+ b; ?# X8 I
±--------------------------------------------±----------------------------------------------------------------+$ }/ B3 q! E" T) y
| addu256(x:u256, y:u256) -> z:u256 | x + y |
±--------------------------------------------±----------------------------------------------------------------+( W2 M M* O* f! X/ k3 w
| subu256(x:u256, y:u256) -> z:u256 | x - y |
±--------------------------------------------±----------------------------------------------------------------+" a3 h. S0 `8 \ U' \& [. U
| mulu256(x:u256, y:u256) -> z:u256 | x * y |
±--------------------------------------------±----------------------------------------------------------------+
| divu256(x:u256, y:u256) -> z:u256 | x / y |% f) F2 A7 s! I! K. N
±--------------------------------------------±----------------------------------------------------------------+# G; b% g( X* W9 K+ ~* P
| divs256(x:s256, y:s256) -> z:s256 | x / y, 有符号数用补码形式 |* E; C* P u, V4 v; ^$ h2 f
±--------------------------------------------±----------------------------------------------------------------+5 W6 V& q ?, r- u: f4 s4 T
| modu256(x:u256, y:u256) -> z:u256 | x % y |% N5 R2 z( Y C V1 G
±--------------------------------------------±----------------------------------------------------------------+
| mods256(x:s256, y:s256) -> z:s256 | x % y, 有符号数用补码形式 |0 r9 p B2 R) o" a, u( r
±--------------------------------------------±----------------------------------------------------------------+
| signextendu256(i:u256, x:u256) -> z:u256 | 从第 (i*8+7) 位开始进行符号扩展,从最低符号位开始计算 |, ^1 O. W! Q3 `
±--------------------------------------------±----------------------------------------------------------------+3 K3 ]; {) f2 K" d4 |9 O
| expu256(x:u256, y:u256) -> z:u256 | x 的 y 次方 |
±--------------------------------------------±----------------------------------------------------------------+! h$ D" @0 m$ G# _% b
| addmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x + y) % m |
±--------------------------------------------±----------------------------------------------------------------+! G0 e% I3 x' c! C& A8 `
| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x * y) % m |" [/ m+ }3 Y$ I/ b
±--------------------------------------------±----------------------------------------------------------------+) s3 X! S% {1 N- p
| ltu256(x:u256, y:u256) -> z:bool | 若 x z:bool | 若 x > y 为 true, 否则为 false |
±--------------------------------------------±----------------------------------------------------------------+/ E6 o2 C; u6 x$ Q7 U
| sltu256(x:s256, y:s256) -> z:bool | 若 x z:bool | 若 x > y 为 true, 否则为 false |
| | 有符号数用补码形式 |9 t( R' t* n$ {5 D) `3 B
±--------------------------------------------±----------------------------------------------------------------+
| equ256(x:u256, y:u256) -> z:bool | 若 x == y 为 true, 否则为 false |4 K3 b! W e) j$ e' ]0 j
±--------------------------------------------±----------------------------------------------------------------+
| iszerou256(x:u256) -> z:bool | 若 x == 0 为 true, 否则为 false |
±--------------------------------------------±----------------------------------------------------------------+
| notu256(x:u256) -> z:u256 | ~x, 对 x 按位非 |/ K1 ?' k) q% U7 [1 ?) \5 ~
±--------------------------------------------±----------------------------------------------------------------+) }7 V0 E$ a2 U3 q
| andu256(x:u256, y:u256) -> z:u256 | x 和 y 按位与 |- I3 ~- H1 I) o' a
±--------------------------------------------±----------------------------------------------------------------+1 E" L( V B( u% v6 B: \& M
| oru256(x:u256, y:u256) -> z:u256 | x 和 y 按位或 |
±--------------------------------------------±----------------------------------------------------------------+
| xoru256(x:u256, y:u256) -> z:u256 | x 和 y 按位异或 | o& V( w6 S: ?+ |7 N: U
±--------------------------------------------±----------------------------------------------------------------+
| shlu256(x:u256, y:u256) -> z:u256 | 将 x 逻辑左移 y 位 |2 D. E2 S1 h4 @" @
±--------------------------------------------±----------------------------------------------------------------+
| shru256(x:u256, y:u256) -> z:u256 | 将 x 逻辑右移 y 位 |$ ?# H; u/ X( t1 b) I' J
±--------------------------------------------±----------------------------------------------------------------+& c, e b7 `/ R
| saru256(x:u256, y:u256) -> z:u256 | 将 x 算术右移 y 位 |! P/ @+ x: j$ n
±--------------------------------------------±----------------------------------------------------------------+
| byte(n:u256, x:u256) -> v:u256 | x 的第 n 字节,这里的索引位置是从 0 开始的; |7 e- v/ L% m* u* d
| | 能否用 and256(shr256(n, x), 0xff) 来替换它, |
| | 并使它在 EVM 后端之外被优化呢? |, M$ N5 l# Y% H- S2 _& m4 N
±--------------------------------------------±----------------------------------------------------------------+9 X. ? Y/ ~ L$ n' Y! E. `
| 内存和存储 |
±--------------------------------------------±----------------------------------------------------------------+7 x6 Q) o( N- y1 {' S, {* U0 q
| mload(p:u256) -> v:u256 | mem[p…(p+32)) |
±--------------------------------------------±----------------------------------------------------------------+
| mstore(p:u256, v:u256) | mem[p…(p+32)) := v |
±--------------------------------------------±----------------------------------------------------------------+
| mstore8(p:u256, v:u256) | mem := v & 0xff - 仅修改单个字节 |
±--------------------------------------------±----------------------------------------------------------------+& \' S2 W, Y. U' H, J! f+ Z1 d
| sload(p:u256) -> v:u256 | storage |; N7 r$ U) u' O3 e9 M- Z" t0 t
±--------------------------------------------±----------------------------------------------------------------+
| sstore(p:u256, v:u256) | storage := v |
±--------------------------------------------±----------------------------------------------------------------+
| msize() -> size:u256 | 内存的大小, 即已访问过的内存的最大下标, |7 y' `, ?3 W: X! Q' O- a3 ~9 b
| | 因为内存扩展的限制(只能按字进行扩展) |5 y5 f2 D9 ]4 P
| | 返回值永远都是 32 字节的倍数 |
±--------------------------------------------±----------------------------------------------------------------+% \# I1 |1 z8 K
| 执行控制 |
±--------------------------------------------±----------------------------------------------------------------+. f/ Y2 G( R e6 i8 ~: d
| create(v:u256, p:u256, s:u256) | 以 mem[p…(p+s)) 上的代码创建一个新合约,发送 |
| | v 个 wei,并返回一个新的地址 |
±--------------------------------------------±----------------------------------------------------------------+
| call(g:u256, a:u256, v:u256, in:u256, | 调用地址 a 上的合约,以 mem[in…(in+insize)) 作为输入 |
| insize:u256, out:u256, | 一并发送 g gas 和 v wei ,以 mem[out…(out+outsize)) |
| outsize:u256) | 作为输出空间。若错误,返回 0 (比如,gas 用光 |
| -> r:u256 | 成功,返回 1 |
±--------------------------------------------±----------------------------------------------------------------+# J+ [0 k/ ]% r
| callcode(g:u256, a:u256, v:u256, in:u256, | 相当于 call 但仅仅使用地址 a 上的代码, |! M$ O4 x6 x% ~4 c
| insize:u256, out:u256, | 而留在当前合约的上下文当中 |
| outsize:u256) -> r:u256 | |. n4 w& l5 W. n" `3 E; @( b
±--------------------------------------------±----------------------------------------------------------------+
| delegatecall(g:u256, a:u256, in:u256, | 相当于 callcode, |; l1 u% }+ l" `- H% B
| insize:u256, out:u256, | 但同时保留 caller |) E# v: R5 a2 k1 v6 p! W
| outsize:u256) -> r:u256 | 和 callvalue |
±--------------------------------------------±----------------------------------------------------------------+8 B8 a+ B! m7 |! q- e! J
| abort() | 终止 (相当于EVM上的非法指令) |; B, B& w1 B! W' b
±--------------------------------------------±----------------------------------------------------------------+: q; {; [. x" H2 l; h- {
| return(p:u256, s:u256) | 终止执行,返回 mem[p…(p+s)) 上的数据 |! b/ w# q+ S2 Q0 _- \2 ]
±--------------------------------------------±----------------------------------------------------------------+
| revert(p:u256, s:u256) | 终止执行,恢复状态变更,返回 mem[p…(p+s)) 上的数据 |4 W1 ^ D8 t Y2 U
±--------------------------------------------±----------------------------------------------------------------+
| selfdestruct(a:u256) | 终止执行,销毁当前合约,并且将余额发送到地址 a |9 J$ Y9 S9 ]" V2 A% p) W
±--------------------------------------------±----------------------------------------------------------------+
| log0(p:u256, s:u256) | 用 mem[p…(p+s)] 上的数据产生日志,但没有 topic |
±--------------------------------------------±----------------------------------------------------------------+
| log1(p:u256, s:u256, t1:u256) | 用 mem[p…(p+s)] 上的数据和 topic t1 产生日志 |
±--------------------------------------------±----------------------------------------------------------------+
| log2(p:u256, s:u256, t1:u256, t2:u256) | 用 mem[p…(p+s)] 上的数据和 topic t1,t2 产生日志 |
±--------------------------------------------±----------------------------------------------------------------+& Y9 l3 h" o4 D" \% p: F% c1 p
| log3(p:u256, s:u256, t1:u256, t2:u256, | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3 产生日志 |
| t3:u256) | |& P$ w* W3 d% G& ^$ _0 v
±--------------------------------------------±----------------------------------------------------------------+
| log4(p:u256, s:u256, t1:u256, t2:u256, | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3,t4 |9 ^6 h0 ]4 m4 o) ^: h0 P
| t3:u256, t4:u256) | 产生日志 |
±--------------------------------------------±----------------------------------------------------------------+
| 状态查询 |
±--------------------------------------------±----------------------------------------------------------------+$ l6 ]3 F/ B! V3 u$ K* k! M5 }
| blockcoinbase() -> address:u256 | 当前的矿工 |+ l, s$ A# ?7 F
±--------------------------------------------±----------------------------------------------------------------+9 v, Q! b; L/ b, a+ U* C
| blockdifficulty() -> difficulty:u256 | 当前区块的难度 |6 N J2 {) x- q, Z6 c# Q( L
±--------------------------------------------±----------------------------------------------------------------+
| blockgaslimit() -> limit:u256 | 当前区块的区块 gas 限制 |
±--------------------------------------------±----------------------------------------------------------------+# | {. V' M/ K' [
| blockhash(b:u256) -> hash:u256 | 区块号为 b 的区块的哈希, |6 f2 B& z; V) F; ~4 R9 \+ Y
| | 仅可用于最近的 256 个区块,不包含当前区块 |
±--------------------------------------------±----------------------------------------------------------------+
| blocknumber() -> block:u256 | 当前区块号 |9 z& ~* e2 I" A0 p; c; @- p
±--------------------------------------------±----------------------------------------------------------------+5 N) \! Z2 ~9 {, H
| blocktimestamp() -> timestamp:u256 | 自 epoch 开始的,当前块的时间戳,以秒为单位 |
±--------------------------------------------±----------------------------------------------------------------+
| txorigin() -> address:u256 | 交易的发送方 |
±--------------------------------------------±----------------------------------------------------------------+
| txgasprice() -> price:u256 | 交易中的 gas 价格 |# R; Q5 l, b, m
±--------------------------------------------±----------------------------------------------------------------+
| gasleft() -> gas:u256 | 还可用于执行的 gas |
±--------------------------------------------±----------------------------------------------------------------+9 Q* Z4 l/ ^' H2 M. y# X/ [ C/ T
| balance(a:u256) -> v:u256 | 地址 a 上的 wei 余额 |6 U$ F- s3 a' s4 W. F4 Q
±--------------------------------------------±----------------------------------------------------------------+4 B4 \* w4 R4 {0 k* N4 P6 Y
| this() -> address:u256 | 当前合约/执行上下文的地址 |
±--------------------------------------------±----------------------------------------------------------------+0 {- V6 ]$ @6 y! u+ Y4 u- R- A1 ? H
| caller() -> address:u256 | 调用的发送方 (不包含委托调用) |' k: T3 h/ G& W& F4 r& T
±--------------------------------------------±----------------------------------------------------------------+
| callvalue() -> v:u256 | 与当前调用一起发送的 wei |
±--------------------------------------------±----------------------------------------------------------------+
| calldataload(p:u256) -> v:u256 | 从 position p 开始的 calldata (32 字节) |
±--------------------------------------------±----------------------------------------------------------------+
| calldatasize() -> v:u256 | 以字节为单位的 calldata 的大小 |
±--------------------------------------------±----------------------------------------------------------------+
| calldatacopy(t:u256, f:u256, s:u256) | 从位置为 f 的 calldata 中,拷贝 s 字节到内存位置 t |
±--------------------------------------------±----------------------------------------------------------------+& o1 I7 O. N/ L* a
| codesize() -> size:u256 | 当前合约/执行上下文的代码大小 |
±--------------------------------------------±----------------------------------------------------------------+2 s6 c: O3 R7 d
| codecopy(t:u256, f:u256, s:u256) | 从 code 位置 f 拷贝 s 字节到内存位置 t |8 F, o' h& Z2 c
±--------------------------------------------±----------------------------------------------------------------+& ^& s; D" W' P
| extcodesize(a:u256) -> size:u256 | 地址 a 上的代码大小 |4 J/ E* ~. S+ E- e2 }1 \: j
±--------------------------------------------±----------------------------------------------------------------+ J2 Y, J- }+ J" ~; A0 R
| extcodecopy(a:u256, t:u256, f:u256, s:u256) | 相当于 codecopy(t, f, s),但从地址 a 获取代码 | ^8 O) j" P% c+ r5 P
±--------------------------------------------±----------------------------------------------------------------+
| 其他 |+ b2 T6 P0 d, S9 F
±--------------------------------------------±----------------------------------------------------------------+
| discard(unused:bool) | 丢弃值 |1 M1 T) [6 A4 g3 ~# Y8 ^; ?
±--------------------------------------------±----------------------------------------------------------------+
| discardu256(unused:u256) | 丢弃值 |
±--------------------------------------------±----------------------------------------------------------------+9 H ]5 J7 z$ o: z, [
| splitu256tou64(x:u256) -> (x1:u64, x2:u64, | 将一个 u256 拆分为四个 u64 |& J) l0 m7 r/ p! b
| x3:u64, x4:u64) | |
±--------------------------------------------±----------------------------------------------------------------+8 D4 c% r9 E, c4 O4 M7 v+ R
| combineu64tou256(x1:u64, x2:u64, x3:u64, | 将四个 u64 组合为一个 u256 |7 N+ X2 J. W- U0 H
| x4:u64) -> (x:u256) | |( t! d" r5 c: o% w# b1 T; [% F
±--------------------------------------------±----------------------------------------------------------------+5 Q# B" J4 K; B$ ~: r N
| keccak256(p:u256, s:u256) -> v:u256 | keccak(mem[p…(p+s))) |
±--------------------------------------------±----------------------------------------------------------------+
后端1 s7 ]7 ?# H. e; q7 m' T8 W
后端或目标负责将 Yul 翻译到特定字节码。 每个后端都可以暴露以后端名称为前缀的函数。 我们为两个建议的后端保留 evm_ 和 ewasm_ 前缀。' G9 x+ ` g S7 l z. L
后端: EVM+ s0 s+ v* K0 C- j! l
目标 |evm| 将具有所有用 evm_ 前缀暴露的 |evm| 底层操作码。
后端: “EVM 1.5”! W1 s2 ~- ]) c/ q
TBD, x) a3 ]8 A2 H4 j1 _
后端: eWASM9 Z3 l( L# n. f% e
TBD
Yul 对象说明 f }% A2 o9 x5 y+ [
语法::
顶层对象 = 'object' '{' 代码? ( 对象 | 数据 )* '}'
对象 = 'object' 字符串字面量 '{' 代码? ( 对象 | 数据 )* '}': J1 i5 r2 @" [5 ]! p
代码 = 'code' 代码块 `+ U% M# a5 K! Q/ |' p0 _* h
数据 = 'data' 字符串字面量 十六进制字面量 p; K' U5 y1 u7 C
十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')& g0 ]& r1 |/ w& K
字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'7 h4 C$ e0 d1 ?; X
在上面,代码块 指的是前一章中解释的 Yul 代码语法中的 代码块。2 }0 \4 T" m) s8 T* }/ m0 |
Yul 对象示例如下:1 q Z. x) {1 I% O' [1 y3 ~! f
…code::1 g7 z' \% g$ w
// 代码由单个对象组成。 单个 “code” 节点是对象的代码。
// 每个(其他)命名的对象或数据部分都被序列化 V0 P$ t+ {. u3 ?2 k
// 并可供特殊内置函数:datacopy / dataoffset / datasize 用于访问
object {. O1 ~ o3 {2 h! N5 {: y
code {* s1 F" i, {6 A I
let size = datasize("runtime")
let offset = allocate(size) p0 T+ r6 t, s* L6 H f
// 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy) U9 c6 g/ c5 ?( k+ K$ i
datacopy(dataoffset("runtime"), offset, size): ?, L$ I( ~4 S$ }# h: S
// 这是一个构造函数,并且运行时代码会被返回6 j- u4 I( v0 G' U. ~) U6 d7 W' d5 m
return(offset, size)
}
data "Table2" hex"4123"
object "runtime" {' }# l' q( m" L4 t/ ` v
code {0 k+ w! W7 R( Q: d+ M
// 运行时代码- o) f* e0 n8 ?+ H
let size = datasize("Contract2")
let offset = allocate(size)
// 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy
datacopy(dataoffset("Contract2"), offset, size)
// 构造函数参数是一个数字 0x1234
mstore(add(offset, size), 0x1234)8 N/ e- `: l; g) u5 ~. P
create(offset, add(size, 32))8 I9 P# k7 r8 n2 P7 Z$ W. J
}
// 内嵌对象。使用场景是,外层是一个工厂合约,而 Contract2 将是由工厂生成的代码
object "Contract2" {1 W4 K/ M# N7 Q% _. u6 U
code {
// 代码在这 ...
}
object "runtime" {' b+ g' I {' D Q5 ^ }" l
code {
// 代码在这 ...5 c7 |% G7 \! r% W
}
}
data "Table1" hex"4123"2 g! S! q( \% A
}
}$ H: j" t p3 t2 V/ V' Y- n0 r
}