正因为如此,它被设计成为这三种平台的可用的共同标准。7 J; R1 b$ C i# j' k% E
它已经可以用于 Solidity 内部的“内联汇编”,并且未来版本的 Solidity 编译器甚至会将 Yul 用作中间语言。 为 Yul 构建高级的优化器阶段也将会很容易。
… note::; c% x; {, R6 A. G8 c Y
请注意,用于“内联汇编”的书写风格是不带类型的(所有的都是 ``u256``),内置函数与 |evm| 操作码相同。
有关详细信息,请参阅内联汇编文档。
Yul 的核心组件是函数,代码块,变量,字面量,for 循环,if 条件语句,switch 条件语句,表达式和变量赋值。
Yul 是强类型的,变量和字面量都需要通过前缀符号来指明类型。支持的类型有:bool, u8, s8, u32, s32,
u64, s64, u128, s128, u256 和 s256。9 v2 a) x7 [. u3 N
Yul 本身甚至不提供操作符。如果目标平台是 |evm|,则操作码将作为内置函数提供,但如果后端平台发生了变化,则可以重新实现它们。
有关强制性的内置函数的列表,请参阅下面的章节。$ v( P7 K+ `( [+ p* k$ A1 R! H
以下示例程序假定 |evm| 操作码 mul,div 和 mo 是原生支持或可以作为函数用以计算指数的。
… code::9 v' w* T ~2 X4 n" V
{4 f5 j+ y* ]) q+ n& j) U
function power(base:u256, exponent:u256) -> result:u256* e2 ^' B% a8 m, O4 [! ?$ F" x
{/ {( G6 E" ?( m+ S' @2 ]& W9 h: b6 F
switch exponent! j/ w! W- a- [( f* F- B+ r
case 0:u256 { result := 1:u256 }
case 1:u256 { result := base }# C" z1 }$ \$ t! A3 R
default:; u2 u! Y$ E( T) U6 \% h
{( R( T1 s% _/ i5 X' J7 M* i2 F
result := power(mul(base, base), div(exponent, 2:u256))
switch mod(exponent, 2:u256)& u, |5 s9 l( t, v5 t
case 1:u256 { result := mul(base, result) }6 v' l, x: n! O. [- B5 B
}$ H( @! z8 M- T' O# I
}
} H! ~& S# l. n$ H6 j( B* a: H
也可用 for 循环代替递归来实现相同的功能。这里,我们需要 |evm| 操作码 lt (小于)和 add 可用。
… code::7 d& ^6 g# K) x1 q9 c- l
{$ G; O( X; n9 O
function power(base:u256, exponent:u256) -> result:u256; a8 h/ l' j+ V
{
result := 1:u256- N4 o: O6 g+ x1 i1 D
for { let i := 0:u256 } lt(i, exponent) { i := add(i, 1:u256) }2 I9 B2 T8 W+ N, c: C9 r! R2 [# u
{
result := mul(result, base)) V8 k; {0 S) ^8 e0 R+ e
}
}! H6 B3 S, b% l8 G
}
Yul 语言说明
本章介绍 Yul 代码。Yul 代码通常放置在一个 Yul 对象中,它将在下一节中介绍。
语法::
代码块 = '{' 语句* '}'+ |% ]% ]: Z) m/ A0 C
语句 =( v6 ?$ `1 L) q
代码块 |5 W" ~$ L' a1 m1 A. u' }
函数定义 |/ D/ M u# G0 \8 z
变量声明 |
赋值 |
表达式 |
Switch |
For 循环 |
循环中断" }7 j+ `4 O7 F) j: G0 b1 R
函数定义 =
'function' 标识符 '(' 带类型的标识符列表? ')'+ K) R* s }& S4 V; A
( '->' 带类型的标识符列表 )? 代码块
变量声明 =
'let' 带类型的标识符列表 ( ':=' 表达式 )?2 ]) z$ k3 E' _, s. z
赋值 =2 Z1 L7 o' H9 T/ o4 _
标识符列表 ':=' 表达式
表达式 =
函数调用 | 标识符 | 字面量: X4 `4 W& u5 K0 q8 y# ~7 G
If 条件语句 =( n+ u0 l2 l. N8 I1 o9 J5 j! A
'if' 表达式 代码块
Switch 条件语句 =: b2 h3 a: x! ~
'switch' 表达式 Case* ( 'default' 代码块 )?; u5 p2 |( ?4 _
Case =
'case' 字面量 代码块
For 循环 =1 \$ u9 h% c1 j
'for' 代码块 表达式 代码块 代码块 C9 G9 n2 o& z( g4 {
循环中断 =+ R* {4 n( ~9 M
'break' | 'continue'
函数调用 = W& ^& ^: T$ W) b- q3 U8 R
标识符 '(' ( 表达式 ( ',' 表达式 )* )? ')'& ]7 Y- {$ @ J6 b+ ^( q3 k5 d
标识符 = [a-zA-Z_$] [a-zA-Z_0-9]*
标识符列表 = 标识符 ( ',' 标识符)*
类型名 = 标识符 | 内置的类型名
内置的类型名 = 'bool' | [us] ( '8' | '32' | '64' | '128' | '256' )
带类型的标识符列表 = 标识符 ':' 类型名 ( ',' 标识符 ':' 类型名 )*6 _- w: g: v- Q5 I# u. E p
字面量 =
(数字字面量 | 字符串字面量 | 十六进制字面量 | True字面量 | False字面量) ':' 类型名
数字字面量 = 十六进制数字 | 十进制数字8 T/ `- k6 u' j- z2 l
十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'$ Z' r( G% o' o. R l1 K2 r
True字面量 = 'true'6 Q4 ~( x8 }% x7 A3 N0 ?8 \1 J5 K
False字面量 = 'false'* A' b$ g+ F- j& Z" a; Z$ B
十六进制数字 = '0x' [0-9a-fA-F]+
十进制数字 = [0-9]+
语法层面的限制
Switches 必须至少有一个 case(包括 default )。
如果表达式的所有可能值都被覆盖了,那么不应该允许使用 default. L0 k. L/ U: E* o* z( W+ @
(即带 bool 表达式的 switch 语句同时具有 true case 和 false case 的情况下不应再有 default 语句)。
每个表达式都求值为零个或多个值。 标识符和字面量求值为一个值,函数调用求值为所调用函数的返回值。
在变量声明和赋值中,右侧表达式(如果存在)求值后,必须得出与左侧变量数量相等的值。, x {9 h' B4 m
这是唯一允许求值出多个值的表达式。
那种同时又是语句的表达式(即在代码块的层次)求值结果必须只有零个值。8 k9 s$ N! c0 C2 V' }' I8 ^. b
在其他所有情况中,表达式求值后必须仅有一个值。
continue 和 break 语句只能用在循环体中,并且必须与循环处于同一个函数中(或者两者都必须在顶层)。% F% g% @) H2 k& L) d
for 循环的条件部分的求值结果只能为一个值。8 s8 m5 E5 B. @' n
字面量不可以大于它们本身的类型。已定义的最大类型宽度为 256 比特。
作用域规则1 w" e$ v3 W* g. V3 ?+ L
Yul 中的作用域是与块(除了函数和 for 循环,如下所述)和所有引入新的标识符到作用域中的声明
( FunctionDefinition ,VariableDeclaration )紧密绑定的。
标识符在将其定义的块中可见(包括所有子节点和子块)。
作为例外,for 循环的 “init” 部分中(第一个块)定义的标识符在 for 循环的所有其他部分(但不在循环之外)中都是可见的。, ^ Q0 B+ Q5 W( H# F
在 for 循环的其他部分声明的标识符遵守常规的作用域语法规则。
函数的参数和返回参数在函数体中可见,并且它们的名称不能相同。3 ?6 _7 q- G& f( Q" p/ ?% A
变量只能在声明后引用。 尤其是,变量不能在它们自己的变量声明的右边被引用。. h' b" k2 H' K. }$ p' g' U/ j
函数可以在声明之前被引用(如果它们是可见的)。) Z. W& p9 b) {% e' ^
Shadowing 是不被允许的,即是说,你不能在同名标识符已经可见的情况下又定义该标识符,即使它是不可访问的。; E( \# A8 u8 \4 W) i2 [8 k5 y
在函数内,不可能访问声明在函数外的变量。 M. k4 o! f+ t
形式规范7 V/ d* l5 z5 ]2 ~; r3 W
我们通过在 AST 的各个节点上提供重载的求值函数 E 来正式指定 Yul。3 B+ U( h7 B7 R j+ ?( x
任何函数都可能有副作用,所以 E 接受两个状态对象和 AST 节点作为它的参数,并返回两个新的状态对象和数量可变的其他值。: Z+ Z/ m( U3 ^, q' r$ U4 S
这两个状态对象是全局状态对象(在 |evm| 的上下文中是 |memory|,|storage| 和区块链的状态)和本地状态对象(局部变量的状态,即 |evm| 中堆栈的某个段)。
如果 AST 节点是一个语句,E 将返回两个状态对象和一个用于 break 和 continue 语句的 “mode”。' I. G# B) {$ Q
如果 AST 节点是表达式,则 E 返回两个状态对象,并返回与表达式求值结果相同数量的值。# b+ M* d1 G( L- u9 h: t# F$ J
在这份高层次的描述中,并没有对全局状态的确切本质进行说明。# m5 ?6 _( |' q4 c3 R5 m2 W
本地状态 L 是标识符 i 到值 v 的映射,表示为 L = v。
对于标识符 v, 我们用 $v 作为标识符的名字。
我们将为 AST 节点使用解构符号。
… code::
E(G, L, : Block) = y$ o, h( [) n# g( ~
let G1, L1, mode = E(G, L, St1, ..., Stn)
let L2 be a restriction of L1 to the identifiers of L2 h7 A3 m- L# E% Z' h) ^
G1, L2, mode
E(G, L, St1, ..., Stn: Statement) =
if n is zero:
G, L, regular
else:3 K' r( a/ [* v3 Q
let G1, L1, mode = E(G, L, St1)' i7 l2 H7 I: o) N2 K V* s, b0 q
if mode is regular then! ~; N; A5 ]: H
E(G1, L1, St2, ..., Stn)4 t( O7 b4 L; \) X0 D
otherwise* A. [2 V/ W( D7 q
G1, L1, mode8 I$ c) c# d, q8 V1 \2 N
E(G, L, FunctionDefinition) =' N( t3 c. q b c& p
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, ..., n1 m3 |2 s' H" h/ D2 w
G, L1, regular9 t* e% @- B. o5 [+ Z
E(G, L, : Assignment) =
let G1, L1, v1, ..., vn = E(G, L, rhs)- k- h' q: y4 @; l2 q0 y% \
let L2 be a copy of L1 where L2[$vari] = vi for i = 1, ..., n3 h( L ?7 C1 @4 v( i4 q0 p. D
G, L2, regular
E(G, L, : ForLoop) = f) w- ]* J; x
if n >= 1:
let G1, L1, mode = E(G, L, i1, ..., in): t0 `6 M' _6 t0 |2 n8 _5 r+ Y
// 由于语法限制,mode 必须是规则的
let G2, L2, mode = E(G1, L1, for {} condition post body)( n% S5 O2 x' k! ^# d# b
// 由于语法限制,mode 必须是规则的
let L3 be the restriction of L2 to only variables of L, m; W9 z- |3 q
G2, L3, regular. m& O# _$ T/ }, Y/ v: Y4 U
else:
let G1, L1, v = E(G, L, condition)
if v is false:
G1, L1, regular/ L1 C! J/ U) e+ F! _4 C! K
else:# q8 u, R1 I) A, X- C+ P
let G2, L2, mode = E(G1, L, body)
if mode is break:) v: t. {. E* v' s! l! k& w! @
G2, L2, regular
else:) v) Z9 \+ _- w8 m- d8 J! k
G3, L3, mode = E(G2, L2, post)* \3 G6 G3 D# J3 u# D3 ` G6 r. E
E(G3, L3, for {} condition post body)$ y }0 p8 t' b& J7 R5 o" p' [
E(G, L, break: BreakContinue) =( J p F3 i& `$ x* @
G, L, break0 |# f3 X; r" K
E(G, L, continue: BreakContinue) =2 x/ V1 F- B+ I6 u4 D& T5 f
G, L, continue
E(G, L, : If) =+ |$ `- H( ]; ~( Q" l8 E
let G0, L0, v = E(G, L, condition); n- b1 b5 Y M% |+ f+ M1 M
if v is true:- i1 ^: B# l, V ~
E(G0, L0, body)
else:
G0, L0, regular; d; N9 C$ Y* v6 q- Y4 l9 u1 ?1 v
E(G, L, : Switch) =; y6 `) V" p- i2 o
E(G, L, switch condition case l1:t1 st1 ... case ln:tn stn default {})# c4 p- h( k$ A5 Q8 s
E(G, L, : Switch) =' r2 n$ |& e2 R8 v4 @
let G0, L0, v = E(G, L, condition)4 F4 t( d$ f* y; W+ ?
// i = 1 .. n! S' Y y' E& Q3 s+ ^
// 对字面量求值,上下文无关" R, p( w) j' ]* l
let _, _, v1 = E(G0, L0, l1)& D' k; d. f3 \; h' q- P
...; a2 e3 n' X1 @& L: r7 N
let _, _, vn = E(G0, L0, ln)
if there exists smallest i such that vi = v:$ S* @8 m% k# j* r
E(G0, L0, sti)
else:- Y( g2 E4 J- t- x0 Y" v
E(G0, L0, st')
E(G, L, : Identifier) =
G, L, L[$name]
E(G, L, : FunctionCall) =; [: K H6 R, v" N
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 ret1, ..., retm block>: X* W o' i. M. }
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),: g, B( J- d" I0 V# e
where hexString decodes l from hex and left-aligns it into 32 bytes
E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l),0 i. |, v" M& C
where utf8EncodeLeftAligned performs a utf8 encoding of l* O0 k4 t9 j2 \; T) s
and aligns it left into 32 bytes3 }# H! o. O9 k! @4 B
E(G, L, n: HexNumber) = G, L, hex(n)
where hex is the hexadecimal decoding function) E% X5 ~' i7 r. T, m+ w
E(G, L, n: DecimalNumber) = G, L, dec(n),, O' k$ `/ p2 V6 K2 ^9 ~4 f' r" u
where dec is the decimal decoding function, m8 F+ T: H4 L1 P
类型转换函数# o5 u! g, t+ F6 G5 N
Yul 不支持隐式类型转换,因此存在提供显式转换的函数。9 l. ^/ u: H$ \) n- u. v- Y% g
在将较大类型转换为较短类型时,如果发生溢出,则可能会发生运行时异常。) t6 ~8 N# p( u- V) o" Z3 b
下列类型的“截取式”转换是允许的:! v6 J' H5 h! P& @
这里的每种类型的转换函数都有一个格式为 to(x:) -> y: 的原型,4 |6 v3 ]' X! }" y" a# a8 p
比如 u32tobool(x:u32) -> y:bool、u256tou32(x:u256) -> y:u32 或 s256tou256(x:s256) -> y:u256。
… note::& h* F- d. M. G6 ^
``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 }`` 实现, B/ Q) R6 y( U
低级函数( I7 F0 x8 x' d& P# x. d, H9 [" ~
以下函数必须可用:
±--------------------------------------------------------------------------------------------------------------+& N k ]! O% b1 M0 A, n( b$ m9 j
| 逻辑操作 | U( s# d) D3 p+ p
±--------------------------------------------±----------------------------------------------------------------+$ d$ W: p1 E: s- f7 Z
| not(x:bool) -> z:bool | 逻辑非 |
±--------------------------------------------±----------------------------------------------------------------+
| and(x:bool, y:bool) -> z:bool | 逻辑与 |; e/ A1 C3 Q3 W/ T) w$ Q
±--------------------------------------------±----------------------------------------------------------------+/ w: J5 }# Z+ Y, M# B
| or(x:bool, y:bool) -> z:bool | 逻辑或 |7 u/ N/ J6 n% a
±--------------------------------------------±----------------------------------------------------------------+* Y) e& o' p0 |6 O3 X% P8 L& S' V
| xor(x:bool, y:bool) -> z:bool | 异或 |/ X; o1 H$ z( B
±--------------------------------------------±----------------------------------------------------------------+! @' \# F: n' l' l. a% q B- m5 S
| 算术操作 |* N1 o* E8 H' F) X9 v k7 ^4 K
±--------------------------------------------±----------------------------------------------------------------+0 m! [( o/ ~* k, a7 z! R
| addu256(x:u256, y:u256) -> z:u256 | x + y |
±--------------------------------------------±----------------------------------------------------------------+8 U- n0 Y E8 H" j+ P
| subu256(x:u256, y:u256) -> z:u256 | x - y |* q) W5 ] Z: g! q0 @
±--------------------------------------------±----------------------------------------------------------------+* ~5 G! w1 `0 L) F8 n. J: N
| mulu256(x:u256, y:u256) -> z:u256 | x * y |
±--------------------------------------------±----------------------------------------------------------------+5 a2 q1 H. a( D
| divu256(x:u256, y:u256) -> z:u256 | x / y |# c4 [! N7 D$ O- m* t
±--------------------------------------------±----------------------------------------------------------------+, ?4 S1 c7 ?/ {& ~' y5 C% I
| divs256(x:s256, y:s256) -> z:s256 | x / y, 有符号数用补码形式 |; o" o/ o( ?) c
±--------------------------------------------±----------------------------------------------------------------+
| modu256(x:u256, y:u256) -> z:u256 | x % y |
±--------------------------------------------±----------------------------------------------------------------+ {1 z* \/ J3 d: Q& ^% @
| mods256(x:s256, y:s256) -> z:s256 | x % y, 有符号数用补码形式 |
±--------------------------------------------±----------------------------------------------------------------+
| signextendu256(i:u256, x:u256) -> z:u256 | 从第 (i*8+7) 位开始进行符号扩展,从最低符号位开始计算 |' a! u3 m* }5 U! p: C) G+ s3 I
±--------------------------------------------±----------------------------------------------------------------+
| expu256(x:u256, y:u256) -> z:u256 | x 的 y 次方 |+ h7 r" T4 J& x' V
±--------------------------------------------±----------------------------------------------------------------+
| addmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x + y) % m |( F) l* y) H- g0 U+ s+ S1 f
±--------------------------------------------±----------------------------------------------------------------+) l/ ], x/ d" D( Z5 B
| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x * y) % m |) Y8 [; q# e/ c9 i7 y2 H/ ~) ]' E
±--------------------------------------------±----------------------------------------------------------------+; P6 ]# s/ }( D* X9 D0 t
| ltu256(x:u256, y:u256) -> z:bool | 若 x z:bool | 若 x > y 为 true, 否则为 false |
±--------------------------------------------±----------------------------------------------------------------+
| sltu256(x:s256, y:s256) -> z:bool | 若 x z:bool | 若 x > y 为 true, 否则为 false |
| | 有符号数用补码形式 |. _& K9 c9 {5 `$ W+ z" p& Z, u
±--------------------------------------------±----------------------------------------------------------------+- W7 {7 t$ p: \5 ?8 |% l
| equ256(x:u256, y:u256) -> z:bool | 若 x == y 为 true, 否则为 false |
±--------------------------------------------±----------------------------------------------------------------+
| iszerou256(x:u256) -> z:bool | 若 x == 0 为 true, 否则为 false |- a+ U5 D# Z. E4 s
±--------------------------------------------±----------------------------------------------------------------+
| notu256(x:u256) -> z:u256 | ~x, 对 x 按位非 |
±--------------------------------------------±----------------------------------------------------------------+- }4 J; o8 D0 o
| andu256(x:u256, y:u256) -> z:u256 | x 和 y 按位与 |
±--------------------------------------------±----------------------------------------------------------------+
| oru256(x:u256, y:u256) -> z:u256 | x 和 y 按位或 |
±--------------------------------------------±----------------------------------------------------------------+
| xoru256(x:u256, y:u256) -> z:u256 | x 和 y 按位异或 |
±--------------------------------------------±----------------------------------------------------------------+3 P# Z f" @) ^2 c: d/ |( C+ M
| shlu256(x:u256, y:u256) -> z:u256 | 将 x 逻辑左移 y 位 |
±--------------------------------------------±----------------------------------------------------------------+% t, l1 l9 Q# `8 V/ Q1 O" L7 Q
| shru256(x:u256, y:u256) -> z:u256 | 将 x 逻辑右移 y 位 |& v4 R. s8 c/ G& A# h1 p( i* t
±--------------------------------------------±----------------------------------------------------------------+
| saru256(x:u256, y:u256) -> z:u256 | 将 x 算术右移 y 位 |
±--------------------------------------------±----------------------------------------------------------------+
| byte(n:u256, x:u256) -> v:u256 | x 的第 n 字节,这里的索引位置是从 0 开始的; |; y* M& \. X( H5 l
| | 能否用 and256(shr256(n, x), 0xff) 来替换它, |" ]4 N$ T; _( m2 `, u8 y
| | 并使它在 EVM 后端之外被优化呢? |
±--------------------------------------------±----------------------------------------------------------------+% [' r6 i: a1 b( i) L: y0 d7 t
| 内存和存储 |1 ` ]' ^ w3 V* C5 |
±--------------------------------------------±----------------------------------------------------------------+1 ~# g# A+ ?: e
| mload(p:u256) -> v:u256 | mem[p…(p+32)) |' f5 P2 t; S* B5 V
±--------------------------------------------±----------------------------------------------------------------+0 c3 g6 x. {( _ ^2 k7 F! Y6 x9 i8 e3 a% ?
| mstore(p:u256, v:u256) | mem[p…(p+32)) := v |! g0 P* E( w3 B- t, ~* u( f" `
±--------------------------------------------±----------------------------------------------------------------+" I5 f) o( s: \+ r& b
| mstore8(p:u256, v:u256) | mem := v & 0xff - 仅修改单个字节 |
±--------------------------------------------±----------------------------------------------------------------+
| sload(p:u256) -> v:u256 | storage |+ X. B1 M/ G3 e" X, j* R: A
±--------------------------------------------±----------------------------------------------------------------+
| sstore(p:u256, v:u256) | storage := v |$ V6 X' C* H3 N: a0 g1 P
±--------------------------------------------±----------------------------------------------------------------+
| msize() -> size:u256 | 内存的大小, 即已访问过的内存的最大下标, |$ F( U8 }0 s U4 x) K$ O
| | 因为内存扩展的限制(只能按字进行扩展) |7 t, s2 p7 Z. u# z3 }: u
| | 返回值永远都是 32 字节的倍数 |
±--------------------------------------------±----------------------------------------------------------------+0 @# [- ?2 p3 w3 Y& p
| 执行控制 |
±--------------------------------------------±----------------------------------------------------------------+
| create(v:u256, p:u256, s:u256) | 以 mem[p…(p+s)) 上的代码创建一个新合约,发送 |7 }" b' a& x1 ~; k
| | v 个 wei,并返回一个新的地址 |
±--------------------------------------------±----------------------------------------------------------------+! t2 x1 G# t: P7 g, j( t& Q/ @8 x
| call(g:u256, a:u256, v:u256, in:u256, | 调用地址 a 上的合约,以 mem[in…(in+insize)) 作为输入 |0 o* o' X$ t( {' o9 [1 C1 e3 Q; o; ?& E. g
| insize:u256, out:u256, | 一并发送 g gas 和 v wei ,以 mem[out…(out+outsize)) |' b. o/ }* L( W. _" X5 f# M1 I
| outsize:u256) | 作为输出空间。若错误,返回 0 (比如,gas 用光 |( j& l! U5 _# O( e7 r
| -> r:u256 | 成功,返回 1 |
±--------------------------------------------±----------------------------------------------------------------+0 r" V2 Q6 ]6 C) a0 T6 G. \% V
| callcode(g:u256, a:u256, v:u256, in:u256, | 相当于 call 但仅仅使用地址 a 上的代码, |. H( I j! _3 G8 A
| insize:u256, out:u256, | 而留在当前合约的上下文当中 |4 Y7 v: M* t7 _6 ]# e% U) t
| outsize:u256) -> r:u256 | |
±--------------------------------------------±----------------------------------------------------------------+
| delegatecall(g:u256, a:u256, in:u256, | 相当于 callcode, |0 u4 k. p' K1 \. z n1 x7 H: A' Z5 {. u
| insize:u256, out:u256, | 但同时保留 caller |( X& k! N! t0 d& n. S' C) {
| outsize:u256) -> r:u256 | 和 callvalue |. }% U* E8 O7 _0 _% \3 Z# m/ a
±--------------------------------------------±----------------------------------------------------------------+
| abort() | 终止 (相当于EVM上的非法指令) |
±--------------------------------------------±----------------------------------------------------------------+
| return(p:u256, s:u256) | 终止执行,返回 mem[p…(p+s)) 上的数据 |
±--------------------------------------------±----------------------------------------------------------------+
| revert(p:u256, s:u256) | 终止执行,恢复状态变更,返回 mem[p…(p+s)) 上的数据 |
±--------------------------------------------±----------------------------------------------------------------+
| selfdestruct(a:u256) | 终止执行,销毁当前合约,并且将余额发送到地址 a |
±--------------------------------------------±----------------------------------------------------------------+
| log0(p:u256, s:u256) | 用 mem[p…(p+s)] 上的数据产生日志,但没有 topic |2 E& D2 h9 C$ J) T# @2 Q
±--------------------------------------------±----------------------------------------------------------------+$ b# [7 P! d m* ]0 Y9 a4 A
| 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 产生日志 |
±--------------------------------------------±----------------------------------------------------------------+
| log3(p:u256, s:u256, t1:u256, t2:u256, | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3 产生日志 |1 t5 _+ I. g( n) J* H
| t3:u256) | |
±--------------------------------------------±----------------------------------------------------------------+4 k9 f1 c" j2 K
| log4(p:u256, s:u256, t1:u256, t2:u256, | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3,t4 |8 ^1 ^- {; ]6 H0 [6 A+ N. u& M
| t3:u256, t4:u256) | 产生日志 |; C8 \& o" E5 V8 g l/ Q4 T
±--------------------------------------------±----------------------------------------------------------------+( f* h* t" }: r* [
| 状态查询 |
±--------------------------------------------±----------------------------------------------------------------+
| blockcoinbase() -> address:u256 | 当前的矿工 |2 R: R. X) k* C: A/ p/ n0 H0 x$ x+ _
±--------------------------------------------±----------------------------------------------------------------+
| blockdifficulty() -> difficulty:u256 | 当前区块的难度 |
±--------------------------------------------±----------------------------------------------------------------+
| blockgaslimit() -> limit:u256 | 当前区块的区块 gas 限制 |
±--------------------------------------------±----------------------------------------------------------------+
| blockhash(b:u256) -> hash:u256 | 区块号为 b 的区块的哈希, |4 q* e2 L' Y8 r1 M; V/ N U. P
| | 仅可用于最近的 256 个区块,不包含当前区块 |
±--------------------------------------------±----------------------------------------------------------------+
| blocknumber() -> block:u256 | 当前区块号 |
±--------------------------------------------±----------------------------------------------------------------++ l; v4 W, Y% c$ E, z
| blocktimestamp() -> timestamp:u256 | 自 epoch 开始的,当前块的时间戳,以秒为单位 |
±--------------------------------------------±----------------------------------------------------------------+( G7 @3 i6 {5 [/ t( T# |. }
| txorigin() -> address:u256 | 交易的发送方 |
±--------------------------------------------±----------------------------------------------------------------+
| txgasprice() -> price:u256 | 交易中的 gas 价格 |
±--------------------------------------------±----------------------------------------------------------------+
| gasleft() -> gas:u256 | 还可用于执行的 gas |
±--------------------------------------------±----------------------------------------------------------------+
| balance(a:u256) -> v:u256 | 地址 a 上的 wei 余额 |, O, j# e+ E* D' F
±--------------------------------------------±----------------------------------------------------------------+
| this() -> address:u256 | 当前合约/执行上下文的地址 |
±--------------------------------------------±----------------------------------------------------------------+
| caller() -> address:u256 | 调用的发送方 (不包含委托调用) |) @% k) Z- R( `
±--------------------------------------------±----------------------------------------------------------------+' |! _ i, |3 R: K& b$ u7 \: R
| callvalue() -> v:u256 | 与当前调用一起发送的 wei |. V: V- q( v+ A7 s8 @
±--------------------------------------------±----------------------------------------------------------------+5 b$ [: L- B. D3 r" |; y9 X# q3 o
| calldataload(p:u256) -> v:u256 | 从 position p 开始的 calldata (32 字节) |
±--------------------------------------------±----------------------------------------------------------------+5 r8 i. V0 E. i: [# u% v+ B5 L
| calldatasize() -> v:u256 | 以字节为单位的 calldata 的大小 |* n$ @, P ? x+ h
±--------------------------------------------±----------------------------------------------------------------+
| calldatacopy(t:u256, f:u256, s:u256) | 从位置为 f 的 calldata 中,拷贝 s 字节到内存位置 t |. ]1 ?1 _8 c- }1 d* P! {, H
±--------------------------------------------±----------------------------------------------------------------+1 z/ p4 u2 t/ H5 X
| codesize() -> size:u256 | 当前合约/执行上下文的代码大小 |
±--------------------------------------------±----------------------------------------------------------------+
| codecopy(t:u256, f:u256, s:u256) | 从 code 位置 f 拷贝 s 字节到内存位置 t |
±--------------------------------------------±----------------------------------------------------------------+
| extcodesize(a:u256) -> size:u256 | 地址 a 上的代码大小 |; U9 l9 z; u* Z
±--------------------------------------------±----------------------------------------------------------------+5 K r' h0 {6 |
| extcodecopy(a:u256, t:u256, f:u256, s:u256) | 相当于 codecopy(t, f, s),但从地址 a 获取代码 |( M; r2 h" t; \* \
±--------------------------------------------±----------------------------------------------------------------+
| 其他 |
±--------------------------------------------±----------------------------------------------------------------+/ i; e% W0 v4 D1 j! y+ z" C3 G* h
| discard(unused:bool) | 丢弃值 |' b% Q; M8 n5 U5 ]! V+ d
±--------------------------------------------±----------------------------------------------------------------+1 o: c6 F* m1 U- F7 @
| discardu256(unused:u256) | 丢弃值 |6 W" ^; |2 D/ ^7 R- j) E
±--------------------------------------------±----------------------------------------------------------------+
| splitu256tou64(x:u256) -> (x1:u64, x2:u64, | 将一个 u256 拆分为四个 u64 |
| x3:u64, x4:u64) | |
±--------------------------------------------±----------------------------------------------------------------+
| combineu64tou256(x1:u64, x2:u64, x3:u64, | 将四个 u64 组合为一个 u256 |( y; h+ T/ G, W4 E* [
| x4:u64) -> (x:u256) | |
±--------------------------------------------±----------------------------------------------------------------+
| keccak256(p:u256, s:u256) -> v:u256 | keccak(mem[p…(p+s))) |
±--------------------------------------------±----------------------------------------------------------------+
后端
后端或目标负责将 Yul 翻译到特定字节码。 每个后端都可以暴露以后端名称为前缀的函数。 我们为两个建议的后端保留 evm_ 和 ewasm_ 前缀。 G4 J1 [4 W; p$ F" I H' o
后端: EVM
目标 |evm| 将具有所有用 evm_ 前缀暴露的 |evm| 底层操作码。% i6 g6 {( B- D( | P3 J3 I
后端: “EVM 1.5”8 t( _) n; E6 B. Q* r
TBD
后端: eWASM
TBD+ t# `9 F$ _3 Q4 P' d/ [, `1 }
Yul 对象说明
语法::
顶层对象 = 'object' '{' 代码? ( 对象 | 数据 )* '}'
对象 = 'object' 字符串字面量 '{' 代码? ( 对象 | 数据 )* '}'% Z" H/ L5 b0 n& s6 v/ ]& n
代码 = 'code' 代码块9 ~5 }5 i7 ?: Y6 ]9 H
数据 = 'data' 字符串字面量 十六进制字面量' s* N2 `* h2 R" n' ]
十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')7 S- c( @) n6 u9 r
字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'' n/ X0 E7 Q! c' t! p+ M/ i- o
在上面,代码块 指的是前一章中解释的 Yul 代码语法中的 代码块。. \5 z$ _# y5 ?( @, u; B
Yul 对象示例如下:5 B) M" H8 ~) h. f7 Z* |6 m
…code::' S$ f, y" F( E* A
// 代码由单个对象组成。 单个 “code” 节点是对象的代码。1 R/ _/ X8 b# Y, y/ }2 }/ A* @
// 每个(其他)命名的对象或数据部分都被序列化
// 并可供特殊内置函数:datacopy / dataoffset / datasize 用于访问3 o4 m8 J6 k: |7 h5 U) I
object {6 a# N* {7 _* d$ s
code {
let size = datasize("runtime")# E8 I3 I# W! V( ^
let offset = allocate(size)+ p5 N) g; a' ~+ q2 M
// 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy# G5 _9 }' [( A3 h, P0 z2 N3 o: W
datacopy(dataoffset("runtime"), offset, size)
// 这是一个构造函数,并且运行时代码会被返回
return(offset, size)( e/ X& B" P5 v4 Y& S% n0 w1 W& T
}
data "Table2" hex"4123"- Z5 V6 L8 i# H) [* v5 w
object "runtime" {6 _& J- { s/ ?# c, n* }" X
code {
// 运行时代码( N4 V4 t: v6 e* o
let size = datasize("Contract2")
let offset = allocate(size)
// 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy4 [+ j9 f3 z- V7 i, A
datacopy(dataoffset("Contract2"), offset, size)5 G/ s& g& o0 _9 d; t, ?! W7 y6 y
// 构造函数参数是一个数字 0x1234
mstore(add(offset, size), 0x1234)
create(offset, add(size, 32))
}
// 内嵌对象。使用场景是,外层是一个工厂合约,而 Contract2 将是由工厂生成的代码$ L1 ]! o m3 `+ ]- A6 F) Q
object "Contract2" {
code {, @$ d6 f. ]# N
// 代码在这 ...% K3 P2 W5 v" f3 X
}
object "runtime" {
code {
// 代码在这 ...
}
}
data "Table1" hex"4123"( Z7 h& y$ B$ k. m% T3 M+ k6 Y
}" P# m! O+ A. f5 {
}5 |. }7 c* V. p
}