正因为如此,它被设计成为这三种平台的可用的共同标准。+ i. n% d! |( J) v
它已经可以用于 Solidity 内部的“内联汇编”,并且未来版本的 Solidity 编译器甚至会将 Yul 用作中间语言。 为 Yul 构建高级的优化器阶段也将会很容易。
… note::0 N6 r$ Y+ A4 `* G: q0 ?
请注意,用于“内联汇编”的书写风格是不带类型的(所有的都是 ``u256``),内置函数与 |evm| 操作码相同。
有关详细信息,请参阅内联汇编文档。6 ^2 m- t* c: w& U# c4 U
Yul 的核心组件是函数,代码块,变量,字面量,for 循环,if 条件语句,switch 条件语句,表达式和变量赋值。# ~9 j0 f: z/ u' X
Yul 是强类型的,变量和字面量都需要通过前缀符号来指明类型。支持的类型有:bool, u8, s8, u32, s32,. c! N, y8 y8 Y! M% k
u64, s64, u128, s128, u256 和 s256。% o. r' ~) U6 B7 p8 K3 w, Q, M
Yul 本身甚至不提供操作符。如果目标平台是 |evm|,则操作码将作为内置函数提供,但如果后端平台发生了变化,则可以重新实现它们。
有关强制性的内置函数的列表,请参阅下面的章节。
以下示例程序假定 |evm| 操作码 mul,div 和 mo 是原生支持或可以作为函数用以计算指数的。/ I7 v* T6 r/ _+ R5 M6 ]/ D3 b
… code::
{& W9 E* U- I- Z
function power(base:u256, exponent:u256) -> result:u256
{
switch exponent
case 0:u256 { result := 1:u256 }5 C2 i, J7 b6 `% F+ s1 k6 I9 F
case 1:u256 { result := base }( |: Q+ p! e+ v9 B
default:( e, W6 N O+ \# Z/ t
{
result := power(mul(base, base), div(exponent, 2:u256))
switch mod(exponent, 2:u256)+ B0 M- D% O% G* z6 V$ y4 O8 f: l; L
case 1:u256 { result := mul(base, result) }6 F) b1 x6 j0 F# n; \! i
}
}9 e4 f* {0 b$ b) v6 w
}7 Y( F& x' b5 H& T$ [* e; I
也可用 for 循环代替递归来实现相同的功能。这里,我们需要 |evm| 操作码 lt (小于)和 add 可用。- g0 |3 g7 b" b' S' G3 E- }0 v
… code::+ Z Q9 M( O/ I8 e5 c2 z; l; I$ s& O
{5 G& h8 z4 Q# L
function power(base:u256, exponent:u256) -> result:u256
{
result := 1:u256
for { let i := 0:u256 } lt(i, exponent) { i := add(i, 1:u256) }/ c4 B+ r1 a& e& G# X! B
{' F! N9 V: W: I* e# c
result := mul(result, base)
}8 ~6 w, J( |, k5 t; f" t
}4 m( y4 U# H3 k) b! @
}3 a) P- T* k7 t. T
Yul 语言说明
本章介绍 Yul 代码。Yul 代码通常放置在一个 Yul 对象中,它将在下一节中介绍。
语法::
代码块 = '{' 语句* '}'
语句 =
代码块 |
函数定义 |
变量声明 |
赋值 |
表达式 |
Switch |- V1 B4 C8 s0 p. l
For 循环 |
循环中断
函数定义 =
'function' 标识符 '(' 带类型的标识符列表? ')'
( '->' 带类型的标识符列表 )? 代码块
变量声明 =% G# R7 L1 _( X; I
'let' 带类型的标识符列表 ( ':=' 表达式 )?" L3 l- M1 P* f8 l& K
赋值 =5 ^' }! i# X! }$ T1 j% Q6 e, H2 [
标识符列表 ':=' 表达式$ U4 E6 |5 S4 {3 a3 H9 b
表达式 =" n. e% ]4 w3 o9 x
函数调用 | 标识符 | 字面量8 w, i, A/ q: l' K5 q: f. x* x
If 条件语句 =! Y6 ~# C8 s5 d( ^0 i5 Q3 M
'if' 表达式 代码块
Switch 条件语句 =% Q$ F: e6 T5 `2 @7 }: z+ S
'switch' 表达式 Case* ( 'default' 代码块 )?+ Y, D* C" a0 D' W* x
Case =; r" E6 U+ |6 C+ t
'case' 字面量 代码块! ]1 @" z1 b: j
For 循环 = o8 `7 y, Q3 l! C, q; X
'for' 代码块 表达式 代码块 代码块1 r7 ]+ f/ O# _' c6 v4 v
循环中断 =
'break' | 'continue'
函数调用 =3 a: V. y I$ U; O
标识符 '(' ( 表达式 ( ',' 表达式 )* )? ')'7 I7 g4 `& n0 a
标识符 = [a-zA-Z_$] [a-zA-Z_0-9]*0 P# {; L! k; @ ^; R7 K
标识符列表 = 标识符 ( ',' 标识符)*
类型名 = 标识符 | 内置的类型名
内置的类型名 = 'bool' | [us] ( '8' | '32' | '64' | '128' | '256' )$ u# }. `! D! u, {& Z0 V
带类型的标识符列表 = 标识符 ':' 类型名 ( ',' 标识符 ':' 类型名 )*. F- d8 T- B: y- y3 ~$ q" o
字面量 =
(数字字面量 | 字符串字面量 | 十六进制字面量 | True字面量 | False字面量) ':' 类型名9 ~! k) U% ?, I6 y: s S. P
数字字面量 = 十六进制数字 | 十进制数字
十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"') a" X4 a3 ~7 f2 q& k
True字面量 = 'true'
False字面量 = 'false'% ^( O* V3 c) E: Z
十六进制数字 = '0x' [0-9a-fA-F]+% S' Q9 e) a' L" g" q9 l, F
十进制数字 = [0-9]+
语法层面的限制
Switches 必须至少有一个 case(包括 default )。
如果表达式的所有可能值都被覆盖了,那么不应该允许使用 default! o% q: \/ H H% h+ l4 w
(即带 bool 表达式的 switch 语句同时具有 true case 和 false case 的情况下不应再有 default 语句)。, d7 H' w" x- `9 ]$ @6 B
每个表达式都求值为零个或多个值。 标识符和字面量求值为一个值,函数调用求值为所调用函数的返回值。
在变量声明和赋值中,右侧表达式(如果存在)求值后,必须得出与左侧变量数量相等的值。' R& A9 L% A% J
这是唯一允许求值出多个值的表达式。
那种同时又是语句的表达式(即在代码块的层次)求值结果必须只有零个值。
在其他所有情况中,表达式求值后必须仅有一个值。( `, {7 x. _4 U n# i1 N2 g8 ?
continue 和 break 语句只能用在循环体中,并且必须与循环处于同一个函数中(或者两者都必须在顶层)。* N4 u) R' A" _' L( M# L$ R! r
for 循环的条件部分的求值结果只能为一个值。' d5 p0 p& d) V- G0 R; P
字面量不可以大于它们本身的类型。已定义的最大类型宽度为 256 比特。9 ~, F# B4 C+ K8 \, a& n
作用域规则
Yul 中的作用域是与块(除了函数和 for 循环,如下所述)和所有引入新的标识符到作用域中的声明3 r% F4 o1 L+ r" X
( FunctionDefinition ,VariableDeclaration )紧密绑定的。
标识符在将其定义的块中可见(包括所有子节点和子块)。
作为例外,for 循环的 “init” 部分中(第一个块)定义的标识符在 for 循环的所有其他部分(但不在循环之外)中都是可见的。+ Z/ ?7 y! |3 J+ r4 F
在 for 循环的其他部分声明的标识符遵守常规的作用域语法规则。5 P# ?3 W' {# k$ p5 g
函数的参数和返回参数在函数体中可见,并且它们的名称不能相同。5 d, d/ e; |* p( j: e- G
变量只能在声明后引用。 尤其是,变量不能在它们自己的变量声明的右边被引用。
函数可以在声明之前被引用(如果它们是可见的)。; R2 m- _4 ?* E0 }) y& u& \
Shadowing 是不被允许的,即是说,你不能在同名标识符已经可见的情况下又定义该标识符,即使它是不可访问的。
在函数内,不可能访问声明在函数外的变量。5 j6 i! ]& {" a9 [2 U6 T5 l
形式规范, P$ C+ o; E$ `4 {% u6 ^& M
我们通过在 AST 的各个节点上提供重载的求值函数 E 来正式指定 Yul。# T6 J+ H" I3 o! q( `" y3 u
任何函数都可能有副作用,所以 E 接受两个状态对象和 AST 节点作为它的参数,并返回两个新的状态对象和数量可变的其他值。
这两个状态对象是全局状态对象(在 |evm| 的上下文中是 |memory|,|storage| 和区块链的状态)和本地状态对象(局部变量的状态,即 |evm| 中堆栈的某个段)。; r8 h# G3 J! o. E7 g2 b3 p
如果 AST 节点是一个语句,E 将返回两个状态对象和一个用于 break 和 continue 语句的 “mode”。7 t4 Q0 I9 \( Z) W* Q$ G
如果 AST 节点是表达式,则 E 返回两个状态对象,并返回与表达式求值结果相同数量的值。8 F% [( e/ |) Z: f7 P
在这份高层次的描述中,并没有对全局状态的确切本质进行说明。
本地状态 L 是标识符 i 到值 v 的映射,表示为 L = v。
对于标识符 v, 我们用 $v 作为标识符的名字。5 K5 Q) A6 n( [& Y
我们将为 AST 节点使用解构符号。" [: o/ ^0 y6 N* j0 l# r8 ]- P5 a
… code::
E(G, L, : Block) =
let G1, L1, mode = E(G, L, St1, ..., Stn)! V2 p* C7 U, a/ z* W
let L2 be a restriction of L1 to the identifiers of L3 S* Y9 e4 V4 s3 v
G1, L2, mode8 u' @0 [1 q5 [2 R1 I4 X9 O0 g/ H8 `
E(G, L, St1, ..., Stn: Statement) =
if n is zero:
G, L, regular
else:% j; m- e% s8 o! V ~
let G1, L1, mode = E(G, L, St1)
if mode is regular then
E(G1, L1, St2, ..., Stn)
otherwise+ [7 @+ G" L0 _$ E( _. [0 y, j
G1, L1, mode
E(G, L, FunctionDefinition) =
G, L, regular) q$ J# q& _: n. ~; x$ K4 Y
E(G, L, : VariableDeclaration) =& ~& H6 ?- n W9 ]- G2 C
E(G, L, : Assignment)( G" S9 I8 B( n6 ]& U" Y8 E
E(G, L, : VariableDeclaration) =6 M0 c' Z% i- V/ d4 Z; n0 ]
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)6 C2 a0 }. i5 f! C5 v
let L2 be a copy of L1 where L2[$vari] = vi for i = 1, ..., n4 }7 `/ P9 l: |. Z8 i6 V
G, L2, regular
E(G, L, : ForLoop) =
if n >= 1:
let G1, L1, mode = E(G, L, i1, ..., in)
// 由于语法限制,mode 必须是规则的# K4 Z4 n1 U2 f, v
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, regular3 F( i# i3 _0 M4 j+ K( c
else:2 L0 e! {$ T0 K- P6 k/ c
let G1, L1, v = E(G, L, condition)+ ?- D9 T4 v9 ^, ~: { ^- j
if v is false:& t I0 p5 g; P& ?4 W
G1, L1, regular/ f7 Z3 A; m' Z! O% j# r
else:! i& G( Y( E! X7 `9 I, {# N! c
let G2, L2, mode = E(G1, L, body)- i7 G5 m) B! a" @- w/ w `5 W8 e3 r
if mode is break:' i8 A% H- X% U
G2, L2, regular
else:
G3, L3, mode = E(G2, L2, post), V ^5 a1 Z3 S0 U k2 F
E(G3, L3, for {} condition post body)
E(G, L, break: BreakContinue) = H" q# ^( @- E3 L, ?
G, L, break9 I. A9 A1 F; K# J: {1 e/ P
E(G, L, continue: BreakContinue) =2 X7 m7 a" e: H) }1 Y, M( f
G, L, continue; R- \# c& M3 M6 F/ C( k2 t
E(G, L, : If) =" J" U/ P" D4 y( a5 f E
let G0, L0, v = E(G, L, condition)
if v is true:
E(G0, L0, body)
else:" w! p+ T9 b2 |0 n2 e
G0, L0, regular5 X% G2 m9 |! a9 @
E(G, L, : Switch) =1 i3 S6 e) S1 q
E(G, L, switch condition case l1:t1 st1 ... case ln:tn stn default {})
E(G, L, : Switch) =/ |# Y! n8 ]" r
let G0, L0, v = E(G, L, condition) h5 {5 k3 Y* C
// i = 1 .. n
// 对字面量求值,上下文无关
let _, _, v1 = E(G0, L0, l1)
...
let _, _, vn = E(G0, L0, ln)
if there exists smallest i such that vi = v:! T6 |. C0 z$ ?! C
E(G0, L0, sti)3 H& z y( q; I K7 c
else:% \. v C9 @$ \6 p! Z0 _
E(G0, L0, st')
E(G, L, : Identifier) =
G, L, L[$name]# Q. @4 w6 m, ~; f8 E: q- I
E(G, L, : FunctionCall) =! L$ ^2 T( K: h" u
G1, L1, vn = E(G, L, argn)
...' Z3 ?. ]# M- ~' |, N
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) j+ U, B: q& ?% K# U, L6 _' N3 g
Let ret1, ..., retm block>5 h* z+ @/ A9 \! R& Y
be the function of name $fname visible at the point of the call.4 @9 u. h! [6 z0 @
Let L' be a new local state such that
L'[$parami] = vi and L'[$reti] = 0 for all i.3 I. i' @, g% |( W
Let G'', L'', mode = E(Gn, L', block)4 r; w0 ^; ^$ C! S$ p7 z" ]: |
G'', Ln, L''[$ret1], ..., L''[$retm]! q( g9 _5 z- v2 N8 p
E(G, L, l: HexLiteral) = G, L, hexString(l),3 q; n$ E) n% I* t' D! k1 h8 G: L, Z1 D
where hexString decodes l from hex and left-aligns it into 32 bytes$ Z. u# {1 B% A* k# P1 X
E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l),9 \7 B+ I( F" m) Z. Q
where utf8EncodeLeftAligned performs a utf8 encoding of l
and aligns it left into 32 bytes1 g. [3 I0 m* O; y8 K
E(G, L, n: HexNumber) = G, L, hex(n)/ ]& B/ v8 |' L# ~
where hex is the hexadecimal decoding function
E(G, L, n: DecimalNumber) = G, L, dec(n),
where dec is the decimal decoding function& ^* o* ?- c0 P* f' s/ k) L
类型转换函数: z8 P( E' G$ t$ H% y& N! f
Yul 不支持隐式类型转换,因此存在提供显式转换的函数。, g8 L6 T6 ^ j# ]
在将较大类型转换为较短类型时,如果发生溢出,则可能会发生运行时异常。4 z. X( E3 T3 F X' T
下列类型的“截取式”转换是允许的:
这里的每种类型的转换函数都有一个格式为 to(x:) -> y: 的原型,8 P0 f. M9 o4 C) e- ^
比如 u32tobool(x:u32) -> y:bool、u256tou32(x:u256) -> y:u32 或 s256tou256(x:s256) -> y:u256。" B7 t5 ?" u$ g- n9 |) i, R2 e
… note::% k" G$ {" W0 B/ ^: B
``u32tobool(x:u32) -> y:bool`` 可以由 ``y := not(iszerou256(x))`` 实现,并且0 `6 A( @, _3 H
``booltou32(x:bool) -> y:u32`` 可以由 ``switch x case true:bool { y := 1:u32 } case false:bool { y := 0:u32 }`` 实现9 E& O& T* F1 r0 G+ f2 N; O
低级函数
以下函数必须可用:
±--------------------------------------------------------------------------------------------------------------+
| 逻辑操作 |4 c, d. |4 L$ S4 C5 l
±--------------------------------------------±----------------------------------------------------------------+% N, I" N& j1 h+ a
| not(x:bool) -> z:bool | 逻辑非 |3 Q* ]* o* y+ B" ~
±--------------------------------------------±----------------------------------------------------------------+* D% q9 E9 W5 l: r% G
| and(x:bool, y:bool) -> z:bool | 逻辑与 |( Y, \. g5 `1 L" T# S& V$ N
±--------------------------------------------±----------------------------------------------------------------+
| or(x:bool, y:bool) -> z:bool | 逻辑或 |
±--------------------------------------------±----------------------------------------------------------------+5 V) N- e1 w! o `5 A1 _% V
| xor(x:bool, y:bool) -> z:bool | 异或 |
±--------------------------------------------±----------------------------------------------------------------+
| 算术操作 |
±--------------------------------------------±----------------------------------------------------------------+6 B/ T& R/ e" \4 B3 n! ~2 u) H
| addu256(x:u256, y:u256) -> z:u256 | x + y |
±--------------------------------------------±----------------------------------------------------------------+; u8 W$ ]# K1 X" h' M) z
| subu256(x:u256, y:u256) -> z:u256 | x - y |3 o# j' t& A" e( F# x0 e2 f
±--------------------------------------------±----------------------------------------------------------------+" x- Y, z5 t6 w
| mulu256(x:u256, y:u256) -> z:u256 | x * y |
±--------------------------------------------±----------------------------------------------------------------+
| divu256(x:u256, y:u256) -> z:u256 | x / y |
±--------------------------------------------±----------------------------------------------------------------+
| divs256(x:s256, y:s256) -> z:s256 | x / y, 有符号数用补码形式 |
±--------------------------------------------±----------------------------------------------------------------+
| modu256(x:u256, y:u256) -> z:u256 | x % y |
±--------------------------------------------±----------------------------------------------------------------+* r" U/ W8 s7 _
| mods256(x:s256, y:s256) -> z:s256 | x % y, 有符号数用补码形式 |
±--------------------------------------------±----------------------------------------------------------------+
| signextendu256(i:u256, x:u256) -> z:u256 | 从第 (i*8+7) 位开始进行符号扩展,从最低符号位开始计算 |
±--------------------------------------------±----------------------------------------------------------------+) H* f5 P* Z& o2 n! S3 ^
| expu256(x:u256, y:u256) -> z:u256 | x 的 y 次方 |$ g" {* S1 K2 t* }! @, B
±--------------------------------------------±----------------------------------------------------------------+% g; a$ a1 S5 M. B% N3 @" X. b
| addmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x + y) % m |" z3 k2 g8 M5 h( w
±--------------------------------------------±----------------------------------------------------------------+
| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x * y) % m |5 }8 T9 y W$ ]5 q6 g/ E
±--------------------------------------------±----------------------------------------------------------------+# H; H9 }: F* H( V, P$ f
| ltu256(x:u256, y:u256) -> z:bool | 若 x z:bool | 若 x > y 为 true, 否则为 false |: J' X8 I9 J0 s/ V6 H/ m5 _5 d
±--------------------------------------------±----------------------------------------------------------------+
| sltu256(x:s256, y:s256) -> z:bool | 若 x z:bool | 若 x > y 为 true, 否则为 false |5 W2 h' b6 G2 |( W2 q, ]
| | 有符号数用补码形式 |" j/ g, ]; j5 S: ~! {$ Q, L
±--------------------------------------------±----------------------------------------------------------------+; f+ O- C) k! B2 S! V4 T" I7 C
| equ256(x:u256, y:u256) -> z:bool | 若 x == y 为 true, 否则为 false |
±--------------------------------------------±----------------------------------------------------------------+9 D* ^7 [; T/ i* g" \$ j0 G% s
| iszerou256(x:u256) -> z:bool | 若 x == 0 为 true, 否则为 false |. L8 a, Q' ~% t$ ]; o! Q! l
±--------------------------------------------±----------------------------------------------------------------+
| notu256(x:u256) -> z:u256 | ~x, 对 x 按位非 |
±--------------------------------------------±----------------------------------------------------------------+
| andu256(x:u256, y:u256) -> z:u256 | x 和 y 按位与 |, [% h, X9 v& j U! D( O1 S2 W( {
±--------------------------------------------±----------------------------------------------------------------+
| oru256(x:u256, y:u256) -> z:u256 | x 和 y 按位或 |
±--------------------------------------------±----------------------------------------------------------------+3 i* w( E% t: P) k) E
| xoru256(x:u256, y:u256) -> z:u256 | x 和 y 按位异或 |
±--------------------------------------------±----------------------------------------------------------------+/ A6 J! i* g7 F) n7 z; h
| shlu256(x:u256, y:u256) -> z:u256 | 将 x 逻辑左移 y 位 |
±--------------------------------------------±----------------------------------------------------------------+
| shru256(x:u256, y:u256) -> z:u256 | 将 x 逻辑右移 y 位 |) X0 |0 ?0 U# i" ]% x+ q# `
±--------------------------------------------±----------------------------------------------------------------+; t( S6 p: h9 O' A. a
| saru256(x:u256, y:u256) -> z:u256 | 将 x 算术右移 y 位 |: w6 |* Z( o0 V; F6 C; i T( d. ~
±--------------------------------------------±----------------------------------------------------------------+
| byte(n:u256, x:u256) -> v:u256 | x 的第 n 字节,这里的索引位置是从 0 开始的; |
| | 能否用 and256(shr256(n, x), 0xff) 来替换它, |
| | 并使它在 EVM 后端之外被优化呢? |
±--------------------------------------------±----------------------------------------------------------------+8 r' f. ]1 P. x! ^
| 内存和存储 |/ |3 s0 }2 @0 o( r' B& R
±--------------------------------------------±----------------------------------------------------------------+
| mload(p:u256) -> v:u256 | mem[p…(p+32)) |
±--------------------------------------------±----------------------------------------------------------------+; m3 x' t( _# p, Z3 e
| mstore(p:u256, v:u256) | mem[p…(p+32)) := v |
±--------------------------------------------±----------------------------------------------------------------+
| mstore8(p:u256, v:u256) | mem := v & 0xff - 仅修改单个字节 |* ~- j* D: j; ?4 M4 N
±--------------------------------------------±----------------------------------------------------------------+
| sload(p:u256) -> v:u256 | storage |
±--------------------------------------------±----------------------------------------------------------------+/ j% z# z1 a" N" H
| sstore(p:u256, v:u256) | storage := v |' I+ x% R7 R. j
±--------------------------------------------±----------------------------------------------------------------+$ A& C6 o4 {- Z4 X5 d
| msize() -> size:u256 | 内存的大小, 即已访问过的内存的最大下标, |
| | 因为内存扩展的限制(只能按字进行扩展) |6 H/ }, z1 V5 f; C# L
| | 返回值永远都是 32 字节的倍数 |. ~- Y/ M7 Q" b0 |% p1 U
±--------------------------------------------±----------------------------------------------------------------+
| 执行控制 |
±--------------------------------------------±----------------------------------------------------------------+1 D$ v7 U9 j/ L" \: ]- ?
| create(v:u256, p:u256, s:u256) | 以 mem[p…(p+s)) 上的代码创建一个新合约,发送 |
| | v 个 wei,并返回一个新的地址 |
±--------------------------------------------±----------------------------------------------------------------+4 V/ b% G+ P/ h* y! H1 y. S& h
| call(g:u256, a:u256, v:u256, in:u256, | 调用地址 a 上的合约,以 mem[in…(in+insize)) 作为输入 |; G; O& X* S; J* l5 s3 P- H
| insize:u256, out:u256, | 一并发送 g gas 和 v wei ,以 mem[out…(out+outsize)) |6 X6 g4 z# V5 v" L! a( @2 T
| outsize:u256) | 作为输出空间。若错误,返回 0 (比如,gas 用光 |; D# b- N5 X8 A0 t @- \
| -> r:u256 | 成功,返回 1 |0 d L& p3 n8 ^8 B- P
±--------------------------------------------±----------------------------------------------------------------+% _! N( _* Q$ P7 K# W
| callcode(g:u256, a:u256, v:u256, in:u256, | 相当于 call 但仅仅使用地址 a 上的代码, |
| insize:u256, out:u256, | 而留在当前合约的上下文当中 |
| outsize:u256) -> r:u256 | |
±--------------------------------------------±----------------------------------------------------------------+8 Q+ E) o% i! v: O
| delegatecall(g:u256, a:u256, in:u256, | 相当于 callcode, |
| insize:u256, out:u256, | 但同时保留 caller |& a3 D1 K2 z W/ |+ M. l
| outsize:u256) -> r:u256 | 和 callvalue |
±--------------------------------------------±----------------------------------------------------------------+
| abort() | 终止 (相当于EVM上的非法指令) |6 e% a. `& @. ^+ h
±--------------------------------------------±----------------------------------------------------------------+
| return(p:u256, s:u256) | 终止执行,返回 mem[p…(p+s)) 上的数据 |; B5 a/ c, M8 e1 c
±--------------------------------------------±----------------------------------------------------------------+
| revert(p:u256, s:u256) | 终止执行,恢复状态变更,返回 mem[p…(p+s)) 上的数据 |
±--------------------------------------------±----------------------------------------------------------------+
| selfdestruct(a:u256) | 终止执行,销毁当前合约,并且将余额发送到地址 a |
±--------------------------------------------±----------------------------------------------------------------+- n. s/ s, U* ^0 N7 D& j5 J
| log0(p:u256, s:u256) | 用 mem[p…(p+s)] 上的数据产生日志,但没有 topic |$ G( w9 j6 L; J& D# [: k0 Q2 r$ }- Q7 R
±--------------------------------------------±----------------------------------------------------------------+
| log1(p:u256, s:u256, t1:u256) | 用 mem[p…(p+s)] 上的数据和 topic t1 产生日志 |
±--------------------------------------------±----------------------------------------------------------------+6 }4 D$ X! q) q; N$ c I) d2 J
| log2(p:u256, s:u256, t1:u256, t2:u256) | 用 mem[p…(p+s)] 上的数据和 topic t1,t2 产生日志 |
±--------------------------------------------±----------------------------------------------------------------+
| log3(p:u256, s:u256, t1:u256, t2:u256, | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3 产生日志 |
| t3:u256) | |6 D$ H- V+ Q5 o1 \: b1 Q0 ?0 c- D0 l
±--------------------------------------------±----------------------------------------------------------------+
| log4(p:u256, s:u256, t1:u256, t2:u256, | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3,t4 |$ S+ o5 [' B' U2 j( T; }% e
| t3:u256, t4:u256) | 产生日志 |
±--------------------------------------------±----------------------------------------------------------------+) T4 Z8 |# ~- }: Z0 x. w. t; t
| 状态查询 |
±--------------------------------------------±----------------------------------------------------------------+
| blockcoinbase() -> address:u256 | 当前的矿工 |
±--------------------------------------------±----------------------------------------------------------------+
| blockdifficulty() -> difficulty:u256 | 当前区块的难度 |5 U, I8 R( C/ ]" A
±--------------------------------------------±----------------------------------------------------------------+
| blockgaslimit() -> limit:u256 | 当前区块的区块 gas 限制 |2 o4 [! p9 Z7 i) C
±--------------------------------------------±----------------------------------------------------------------+
| blockhash(b:u256) -> hash:u256 | 区块号为 b 的区块的哈希, |) g) g; F4 b: K1 j7 z- T" C! `" q
| | 仅可用于最近的 256 个区块,不包含当前区块 |2 T, f! k0 T# B8 P; g
±--------------------------------------------±----------------------------------------------------------------+7 T3 W) _ D' W+ J/ P0 Z0 k# t% r6 `7 E( `
| blocknumber() -> block:u256 | 当前区块号 |" y' _: H. }' S C
±--------------------------------------------±----------------------------------------------------------------+ @1 U/ @+ e6 t R2 w, A/ @
| blocktimestamp() -> timestamp:u256 | 自 epoch 开始的,当前块的时间戳,以秒为单位 |$ c: k! V8 F, \4 S
±--------------------------------------------±----------------------------------------------------------------+
| txorigin() -> address:u256 | 交易的发送方 |- @+ h8 P9 ]( v
±--------------------------------------------±----------------------------------------------------------------+3 R, u$ s$ A3 M6 S/ o* x# L% i
| txgasprice() -> price:u256 | 交易中的 gas 价格 |
±--------------------------------------------±----------------------------------------------------------------+
| gasleft() -> gas:u256 | 还可用于执行的 gas |6 ?9 w) N# l$ q! M" P* G
±--------------------------------------------±----------------------------------------------------------------+
| balance(a:u256) -> v:u256 | 地址 a 上的 wei 余额 |
±--------------------------------------------±----------------------------------------------------------------+
| this() -> address:u256 | 当前合约/执行上下文的地址 |0 h7 L% k9 e8 p$ {
±--------------------------------------------±----------------------------------------------------------------+
| caller() -> address:u256 | 调用的发送方 (不包含委托调用) |
±--------------------------------------------±----------------------------------------------------------------+( Y# G- B+ P- s, J, j/ l
| callvalue() -> v:u256 | 与当前调用一起发送的 wei |- b% k& c; w* S2 A
±--------------------------------------------±----------------------------------------------------------------+
| calldataload(p:u256) -> v:u256 | 从 position p 开始的 calldata (32 字节) |0 c/ D. r+ Z: L
±--------------------------------------------±----------------------------------------------------------------+
| calldatasize() -> v:u256 | 以字节为单位的 calldata 的大小 |
±--------------------------------------------±----------------------------------------------------------------+
| calldatacopy(t:u256, f:u256, s:u256) | 从位置为 f 的 calldata 中,拷贝 s 字节到内存位置 t |
±--------------------------------------------±----------------------------------------------------------------+3 K$ f* ^! t: `. D2 Y
| codesize() -> size:u256 | 当前合约/执行上下文的代码大小 |
±--------------------------------------------±----------------------------------------------------------------+" `# O |! u: Y' K! P
| codecopy(t:u256, f:u256, s:u256) | 从 code 位置 f 拷贝 s 字节到内存位置 t |
±--------------------------------------------±----------------------------------------------------------------+
| extcodesize(a:u256) -> size:u256 | 地址 a 上的代码大小 |$ T" d: |" D3 n* j8 B& E) `
±--------------------------------------------±----------------------------------------------------------------+, F$ A; p) k, Z' f/ c. F
| extcodecopy(a:u256, t:u256, f:u256, s:u256) | 相当于 codecopy(t, f, s),但从地址 a 获取代码 |
±--------------------------------------------±----------------------------------------------------------------+
| 其他 |' L' M: r# z9 W- ^9 Y$ `* w
±--------------------------------------------±----------------------------------------------------------------+
| discard(unused:bool) | 丢弃值 |
±--------------------------------------------±----------------------------------------------------------------+
| discardu256(unused:u256) | 丢弃值 |. X7 G4 b! g, \; _7 o
±--------------------------------------------±----------------------------------------------------------------+4 q4 ^% u8 U" ~6 N
| splitu256tou64(x:u256) -> (x1:u64, x2:u64, | 将一个 u256 拆分为四个 u64 |5 E& W( l' r2 @) Q
| x3:u64, x4:u64) | |5 V8 X% Q- ]0 t8 ]1 S
±--------------------------------------------±----------------------------------------------------------------++ ^- o+ U' U7 |3 Y0 \' C+ r
| combineu64tou256(x1:u64, x2:u64, x3:u64, | 将四个 u64 组合为一个 u256 |
| x4:u64) -> (x:u256) | |
±--------------------------------------------±----------------------------------------------------------------+
| keccak256(p:u256, s:u256) -> v:u256 | keccak(mem[p…(p+s))) |
±--------------------------------------------±----------------------------------------------------------------+
后端- N$ @4 y) g( m6 E5 m5 y0 Z$ _
后端或目标负责将 Yul 翻译到特定字节码。 每个后端都可以暴露以后端名称为前缀的函数。 我们为两个建议的后端保留 evm_ 和 ewasm_ 前缀。
后端: EVM
目标 |evm| 将具有所有用 evm_ 前缀暴露的 |evm| 底层操作码。6 e7 s6 A' ^ o' P
后端: “EVM 1.5”. @2 o% k7 }& e6 R
TBD+ x- F n0 f7 ^: ^" ?) b, h+ C) G
后端: eWASM8 e0 O0 ^) t8 o( n# T) V: l
TBD0 z3 k9 t+ J: u( P2 K
Yul 对象说明+ M8 K; ~3 S }1 D' J. L
语法::
顶层对象 = 'object' '{' 代码? ( 对象 | 数据 )* '}'
对象 = 'object' 字符串字面量 '{' 代码? ( 对象 | 数据 )* '}'
代码 = 'code' 代码块
数据 = 'data' 字符串字面量 十六进制字面量, j: {: L1 v! w# p
十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'! |0 A0 T# d q6 {* s
在上面,代码块 指的是前一章中解释的 Yul 代码语法中的 代码块。
Yul 对象示例如下:# F& _/ t( {5 X+ h
…code::
// 代码由单个对象组成。 单个 “code” 节点是对象的代码。
// 每个(其他)命名的对象或数据部分都被序列化
// 并可供特殊内置函数:datacopy / dataoffset / datasize 用于访问# f% ? n5 i, ~0 C, Y3 E
object {" u4 w' r6 f4 I0 R
code {) _. F5 q$ q: D
let size = datasize("runtime")
let offset = allocate(size)$ V2 {7 g/ H; q, B3 T
// 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy* l. B# C# ?: j" `
datacopy(dataoffset("runtime"), offset, size)
// 这是一个构造函数,并且运行时代码会被返回6 H- ?& \) K: @5 j2 X! X6 x2 v
return(offset, size)% p, ^) j* A3 r, p
}
data "Table2" hex"4123"
object "runtime" {
code {
// 运行时代码. T; r4 @5 }1 O0 `# K% q0 V
let size = datasize("Contract2")& {* ~8 t4 k) x& g+ c* U5 j0 p( t, F3 f
let offset = allocate(size)
// 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy
datacopy(dataoffset("Contract2"), offset, size)# l* h5 [0 l# c8 b: g4 z# _
// 构造函数参数是一个数字 0x1234+ U1 X0 R( z% A+ s$ B
mstore(add(offset, size), 0x1234)
create(offset, add(size, 32))
}; n5 p+ n3 m5 Z
// 内嵌对象。使用场景是,外层是一个工厂合约,而 Contract2 将是由工厂生成的代码
object "Contract2" {/ G, o! V8 Q' W3 o
code {
// 代码在这 ...- w+ b) @( q ?( ]
}
object "runtime" {
code {1 ~$ ~% E, Y- c5 p0 }
// 代码在这 ...5 f+ u) a }. K- p! ~- ?; ]. [$ Q
}
}# E0 T# J G) p
data "Table1" hex"4123"
}) O) {/ I& x. V: { L
}" V. I3 h, ^( V
}