正因为如此,它被设计成为这三种平台的可用的共同标准。! D$ n% d! ]9 f! ?: D3 ~# i, V( {
它已经可以用于 Solidity 内部的“内联汇编”,并且未来版本的 Solidity 编译器甚至会将 Yul 用作中间语言。 为 Yul 构建高级的优化器阶段也将会很容易。
… note::# i- S3 a1 v- z3 Z3 z
请注意,用于“内联汇编”的书写风格是不带类型的(所有的都是 ``u256``),内置函数与 |evm| 操作码相同。
有关详细信息,请参阅内联汇编文档。
Yul 的核心组件是函数,代码块,变量,字面量,for 循环,if 条件语句,switch 条件语句,表达式和变量赋值。) [% {9 q4 S/ {5 v1 O' ?
Yul 是强类型的,变量和字面量都需要通过前缀符号来指明类型。支持的类型有:bool, u8, s8, u32, s32,
u64, s64, u128, s128, u256 和 s256。" c: N. ^% z/ Q, O; k( m' e$ s5 q
Yul 本身甚至不提供操作符。如果目标平台是 |evm|,则操作码将作为内置函数提供,但如果后端平台发生了变化,则可以重新实现它们。
有关强制性的内置函数的列表,请参阅下面的章节。% m/ \$ q( R/ P) ~* v z7 y p+ e. ?
以下示例程序假定 |evm| 操作码 mul,div 和 mo 是原生支持或可以作为函数用以计算指数的。7 q# N( z5 f9 [" I q% A
… code::
{
function power(base:u256, exponent:u256) -> result:u2561 U( v3 E2 W" b C
{
switch exponent2 u! s% ~/ _5 ]7 E2 F
case 0:u256 { result := 1:u256 }- l( t' c: b6 {
case 1:u256 { result := base }
default:, d. s, m7 {+ u" l2 j) L& r
{# Q/ a" Z% D) N, Z& [9 t
result := power(mul(base, base), div(exponent, 2:u256))
switch mod(exponent, 2:u256)
case 1:u256 { result := mul(base, result) }: G# s1 A) [/ z2 Q; V
}
}0 ]$ H7 i/ k- i. }) c4 b1 l
}2 O% |9 L/ T: K; a* V& k
也可用 for 循环代替递归来实现相同的功能。这里,我们需要 |evm| 操作码 lt (小于)和 add 可用。& g4 {" _* m+ h4 i% d
… code::
{
function power(base:u256, exponent:u256) -> result:u256
{
result := 1:u256
for { let i := 0:u256 } lt(i, exponent) { i := add(i, 1:u256) }
{
result := mul(result, base)/ }: @! w; W: X! T/ C7 D
}2 z& k4 }" G; F0 e5 ?" K- ^; ?5 y, R
}
}, g& W9 H( }) f( _
Yul 语言说明7 _( c+ A, L2 h4 d( A% e
本章介绍 Yul 代码。Yul 代码通常放置在一个 Yul 对象中,它将在下一节中介绍。
语法::! g% O; d8 X/ d* ^' s8 ]0 i, _/ c
代码块 = '{' 语句* '}'; W$ n1 A7 w( v! m
语句 =
代码块 |
函数定义 |! X5 h1 I% E( z
变量声明 |
赋值 |
表达式 |
Switch |
For 循环 |
循环中断/ T+ r; S5 ~. y! p# K/ ?
函数定义 =2 c( a% p# b& B: r( d* T
'function' 标识符 '(' 带类型的标识符列表? ')'0 L* [- z, f# o8 u& m& l
( '->' 带类型的标识符列表 )? 代码块. W4 B9 o5 r. `0 J( ^- ?+ d
变量声明 =
'let' 带类型的标识符列表 ( ':=' 表达式 )?
赋值 =
标识符列表 ':=' 表达式
表达式 =8 r% y1 t1 o5 m3 [0 p- u
函数调用 | 标识符 | 字面量/ a$ P: X3 ?* _% Y
If 条件语句 =" M& x& l" Z6 U3 Z2 T- |) C
'if' 表达式 代码块 C' l) ~) T, }& Q
Switch 条件语句 =# M& f1 O' ^ J
'switch' 表达式 Case* ( 'default' 代码块 )?1 L/ t* N$ W; ~. ^: y( V! K& H
Case =" [/ s) X/ B2 X3 [2 n
'case' 字面量 代码块
For 循环 =
'for' 代码块 表达式 代码块 代码块 h$ v- U6 I" H y
循环中断 =1 d. ?: n$ \* G# G& z+ Z A
'break' | 'continue': ?9 b9 y2 s# |& _/ u- s
函数调用 =
标识符 '(' ( 表达式 ( ',' 表达式 )* )? ')'
标识符 = [a-zA-Z_$] [a-zA-Z_0-9]*7 F" r( I1 L* B5 g4 v3 |
标识符列表 = 标识符 ( ',' 标识符)*" Q @2 e9 A4 P& B, b7 Q
类型名 = 标识符 | 内置的类型名1 O$ V- f' _# H+ _# C
内置的类型名 = 'bool' | [us] ( '8' | '32' | '64' | '128' | '256' )) z8 V$ d- b6 I; G" _0 T( c
带类型的标识符列表 = 标识符 ':' 类型名 ( ',' 标识符 ':' 类型名 )*
字面量 =; z" x: V _4 a. l: D7 \+ y4 `2 E
(数字字面量 | 字符串字面量 | 十六进制字面量 | True字面量 | False字面量) ':' 类型名
数字字面量 = 十六进制数字 | 十进制数字# N1 K& q) W$ H' ] s
十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'
True字面量 = 'true'
False字面量 = 'false'
十六进制数字 = '0x' [0-9a-fA-F]+- I. [; N* H3 ]9 l' C
十进制数字 = [0-9]+
语法层面的限制
Switches 必须至少有一个 case(包括 default )。9 o' G- ^( _. i
如果表达式的所有可能值都被覆盖了,那么不应该允许使用 default
(即带 bool 表达式的 switch 语句同时具有 true case 和 false case 的情况下不应再有 default 语句)。
每个表达式都求值为零个或多个值。 标识符和字面量求值为一个值,函数调用求值为所调用函数的返回值。
在变量声明和赋值中,右侧表达式(如果存在)求值后,必须得出与左侧变量数量相等的值。! n3 z7 ?# G% U* c+ t
这是唯一允许求值出多个值的表达式。! b" k0 s+ ^, S; Q
那种同时又是语句的表达式(即在代码块的层次)求值结果必须只有零个值。4 r L, G8 V6 E2 |; k$ l5 F
在其他所有情况中,表达式求值后必须仅有一个值。
continue 和 break 语句只能用在循环体中,并且必须与循环处于同一个函数中(或者两者都必须在顶层)。/ x$ u( E. }6 z& e! a9 h
for 循环的条件部分的求值结果只能为一个值。8 S6 D" Q) ^3 D$ i4 R. c8 R* Q
字面量不可以大于它们本身的类型。已定义的最大类型宽度为 256 比特。
作用域规则3 |/ i m: J% I; l8 h5 e( {
Yul 中的作用域是与块(除了函数和 for 循环,如下所述)和所有引入新的标识符到作用域中的声明$ p. i/ G% B e; g" V2 X
( FunctionDefinition ,VariableDeclaration )紧密绑定的。
标识符在将其定义的块中可见(包括所有子节点和子块)。
作为例外,for 循环的 “init” 部分中(第一个块)定义的标识符在 for 循环的所有其他部分(但不在循环之外)中都是可见的。+ o# [) y2 s& l: U$ ^3 B9 v# d
在 for 循环的其他部分声明的标识符遵守常规的作用域语法规则。
函数的参数和返回参数在函数体中可见,并且它们的名称不能相同。3 p) N8 K' a( [% {( {* K4 G% [0 t
变量只能在声明后引用。 尤其是,变量不能在它们自己的变量声明的右边被引用。
函数可以在声明之前被引用(如果它们是可见的)。
Shadowing 是不被允许的,即是说,你不能在同名标识符已经可见的情况下又定义该标识符,即使它是不可访问的。
在函数内,不可能访问声明在函数外的变量。$ S. N2 F/ L3 F- d( S
形式规范
我们通过在 AST 的各个节点上提供重载的求值函数 E 来正式指定 Yul。
任何函数都可能有副作用,所以 E 接受两个状态对象和 AST 节点作为它的参数,并返回两个新的状态对象和数量可变的其他值。, C) @% ]4 N- {6 e& Q$ X/ `2 e
这两个状态对象是全局状态对象(在 |evm| 的上下文中是 |memory|,|storage| 和区块链的状态)和本地状态对象(局部变量的状态,即 |evm| 中堆栈的某个段)。8 r$ i7 [5 g0 J8 ^% U: c. V* Q
如果 AST 节点是一个语句,E 将返回两个状态对象和一个用于 break 和 continue 语句的 “mode”。
如果 AST 节点是表达式,则 E 返回两个状态对象,并返回与表达式求值结果相同数量的值。9 W8 U6 U1 |3 {( @( I
在这份高层次的描述中,并没有对全局状态的确切本质进行说明。/ E/ y2 |. r9 B9 @
本地状态 L 是标识符 i 到值 v 的映射,表示为 L = v。' e, X6 b; S4 m" ~3 ?0 b
对于标识符 v, 我们用 $v 作为标识符的名字。0 p" w7 y3 Y# Z0 J5 h- W: @7 [% D
我们将为 AST 节点使用解构符号。7 u" x" C0 l; n" K1 B
… code::
E(G, L, : Block) =
let G1, L1, mode = E(G, L, St1, ..., Stn)
let L2 be a restriction of L1 to the identifiers of L
G1, L2, mode
E(G, L, St1, ..., Stn: Statement) =9 Q. [# }, [0 o0 c E/ H4 y
if n is zero:
G, L, regular
else:
let G1, L1, mode = E(G, L, St1)
if mode is regular then
E(G1, L1, St2, ..., Stn)9 Q# R4 m$ A: n/ K( ?( {8 J
otherwise
G1, L1, mode
E(G, L, FunctionDefinition) =. R0 a' `# x0 A- c1 w. `: r8 X
G, L, regular
E(G, L, : VariableDeclaration) =
E(G, L, : Assignment)0 o# O( i& E# X
E(G, L, : VariableDeclaration) =
let L1 be a copy of L where L1[$vari] = 0 for i = 1, ..., n* y, i3 J& }2 u3 c; F
G, L1, regular
E(G, L, : Assignment) =# |$ h* \7 ^4 e) r! } u
let G1, L1, v1, ..., vn = E(G, L, rhs)6 _! I) \% ~ u% K& R- A+ |$ K
let L2 be a copy of L1 where L2[$vari] = vi for i = 1, ..., n
G, L2, regular
E(G, L, : ForLoop) =
if n >= 1:
let G1, L1, mode = E(G, L, i1, ..., in)
// 由于语法限制,mode 必须是规则的* K! N% K; L( h6 b$ ^
let G2, L2, mode = E(G1, L1, for {} condition post body)6 U( ^, {1 N/ U" M
// 由于语法限制,mode 必须是规则的
let L3 be the restriction of L2 to only variables of L
G2, L3, regular ~8 _' I( L1 B& \% J3 }
else:6 I# f' N n+ e
let G1, L1, v = E(G, L, condition)! |" i: {3 {* s( S, p- Q
if v is false:
G1, L1, regular
else:
let G2, L2, mode = E(G1, L, body)/ o1 \5 O; A3 ` p/ n6 U
if mode is break:
G2, L2, regular
else:
G3, L3, mode = E(G2, L2, post) t. M5 C! c/ M( Q7 g. T: z# D
E(G3, L3, for {} condition post body)* l" h$ ^2 z: Q3 o8 s4 c
E(G, L, break: BreakContinue) =: u" B- T* F7 \
G, L, break$ x' D) l- X+ r4 Z( w5 }
E(G, L, continue: BreakContinue) =' R, b. m" B& b; P1 ]
G, L, continue
E(G, L, : If) =
let G0, L0, v = E(G, L, condition)5 z/ d9 C) j9 G+ S# K: R
if v is true:6 T$ ^- R8 w7 i8 b/ A6 v( e ~
E(G0, L0, body)
else:8 q0 I; ~2 }( Q9 \ Q
G0, L0, regular
E(G, L, : Switch) =
E(G, L, switch condition case l1:t1 st1 ... case ln:tn stn default {})
E(G, L, : Switch) =0 i5 E. j5 \0 b$ {) e( |
let G0, L0, v = E(G, L, condition)5 b: F: L, |$ } |- Y
// i = 1 .. n
// 对字面量求值,上下文无关. j8 T6 Z5 z i, e# h' f& w
let _, _, v1 = E(G0, L0, l1), {7 w+ e% T6 U& H
...
let _, _, vn = E(G0, L0, ln)) a( c* {+ f/ c3 i9 i, k7 Y5 l
if there exists smallest i such that vi = v:' P/ t% Y; R: n2 P; g- @
E(G0, L0, sti)
else:+ B' @) t$ H0 C/ _6 ?9 Z. o
E(G0, L0, st')
E(G, L, : Identifier) =
G, L, L[$name]
E(G, L, : FunctionCall) =
G1, L1, vn = E(G, L, argn)2 _4 M# v+ Z4 G' B0 e Y8 n+ L- j
...
G(n-1), L(n-1), v2 = E(G(n-2), L(n-2), arg2)4 Z( i; o7 l& R6 x+ t
Gn, Ln, v1 = E(G(n-1), L(n-1), arg1)( h6 u" M8 B0 g0 j
Let ret1, ..., retm block>% t: |; \: _7 w; w% U& R' ^
be the function of name $fname visible at the point of the call.
Let L' be a new local state such that8 L% N9 I. ]8 {/ F' w
L'[$parami] = vi and L'[$reti] = 0 for all i.
Let G'', L'', mode = E(Gn, L', block)0 q6 s" q$ R U5 H2 Q
G'', Ln, L''[$ret1], ..., L''[$retm]! S& X. K/ {7 M G& J' u
E(G, L, l: HexLiteral) = G, L, hexString(l),
where hexString decodes l from hex and left-aligns it into 32 bytes4 s. l% Y. X+ Y
E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l),6 i/ ^. K8 p5 \ x
where utf8EncodeLeftAligned performs a utf8 encoding of l3 I- d8 ?2 D' L( \9 F: P* q
and aligns it left into 32 bytes
E(G, L, n: HexNumber) = G, L, hex(n)
where hex is the hexadecimal decoding function1 ]9 x, u/ Q$ i4 Z! O) \
E(G, L, n: DecimalNumber) = G, L, dec(n),. ?3 J* d) Q) `7 m+ ]
where dec is the decimal decoding function
类型转换函数, N0 Q8 g$ T$ ], o; _: U6 k
Yul 不支持隐式类型转换,因此存在提供显式转换的函数。
在将较大类型转换为较短类型时,如果发生溢出,则可能会发生运行时异常。/ M. l% M' @/ |- ?3 q1 x( h9 k
下列类型的“截取式”转换是允许的:
这里的每种类型的转换函数都有一个格式为 to(x:) -> y: 的原型,
比如 u32tobool(x:u32) -> y:bool、u256tou32(x:u256) -> y:u32 或 s256tou256(x:s256) -> y:u256。) y5 P4 l. ^4 T& ^7 q7 \
… note::
``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 }`` 实现
低级函数
以下函数必须可用:
±--------------------------------------------------------------------------------------------------------------+
| 逻辑操作 |, H$ Q6 ] Z) m! W$ R
±--------------------------------------------±----------------------------------------------------------------+
| not(x:bool) -> z:bool | 逻辑非 |1 \% E* S5 F" J5 R$ t+ ]
±--------------------------------------------±----------------------------------------------------------------+7 Z% k$ N/ d* \3 D& D
| and(x:bool, y:bool) -> z:bool | 逻辑与 | y! R( L$ ?9 h: Z; d* F
±--------------------------------------------±----------------------------------------------------------------+
| or(x:bool, y:bool) -> z:bool | 逻辑或 |
±--------------------------------------------±----------------------------------------------------------------+% R) k9 S4 U& K: p0 V
| xor(x:bool, y:bool) -> z:bool | 异或 |
±--------------------------------------------±----------------------------------------------------------------+' _! n$ a* [# {
| 算术操作 |
±--------------------------------------------±----------------------------------------------------------------+
| addu256(x:u256, y:u256) -> z:u256 | x + y |
±--------------------------------------------±----------------------------------------------------------------+
| subu256(x:u256, y:u256) -> z:u256 | x - y |$ @# g3 p' r& x; n
±--------------------------------------------±----------------------------------------------------------------+; E7 ^5 a4 B. B, X. K/ b6 b' Q
| mulu256(x:u256, y:u256) -> z:u256 | x * y |5 J8 Q( @# `- ^4 [, Z
±--------------------------------------------±----------------------------------------------------------------+6 ]$ \9 {1 ^& m g* w" @
| divu256(x:u256, y:u256) -> z:u256 | x / y |
±--------------------------------------------±----------------------------------------------------------------+5 H2 r7 L: `( \9 Q9 k! a
| divs256(x:s256, y:s256) -> z:s256 | x / y, 有符号数用补码形式 |0 i( A: d y" D, `
±--------------------------------------------±----------------------------------------------------------------+
| modu256(x:u256, y:u256) -> z:u256 | x % y |- R) O" f& m) ~+ ~
±--------------------------------------------±----------------------------------------------------------------+5 y- H0 \$ E! i- T5 f9 w: j
| mods256(x:s256, y:s256) -> z:s256 | x % y, 有符号数用补码形式 |7 G8 H" i) d/ h3 [9 W$ P z
±--------------------------------------------±----------------------------------------------------------------+( s# \- Y3 v8 y9 L1 W% E
| signextendu256(i:u256, x:u256) -> z:u256 | 从第 (i*8+7) 位开始进行符号扩展,从最低符号位开始计算 |
±--------------------------------------------±----------------------------------------------------------------+, [3 T1 b4 ]8 g1 b7 N* G# v% m
| expu256(x:u256, y:u256) -> z:u256 | x 的 y 次方 |( ~6 j4 P3 `$ v9 t1 U! k; G4 E
±--------------------------------------------±----------------------------------------------------------------+* |6 @. [9 Z5 [5 B8 p1 M
| addmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x + y) % m |
±--------------------------------------------±----------------------------------------------------------------+
| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x * y) % m |# Z3 A% z- B6 y) {" P& Z
±--------------------------------------------±----------------------------------------------------------------+
| ltu256(x:u256, y:u256) -> z:bool | 若 x z:bool | 若 x > y 为 true, 否则为 false |. g! h$ \# W# [8 P+ y' I1 O# |- o4 O4 s! C
±--------------------------------------------±----------------------------------------------------------------+
| sltu256(x:s256, y:s256) -> z:bool | 若 x z:bool | 若 x > y 为 true, 否则为 false |
| | 有符号数用补码形式 |* a) z0 G, E( E# N `
±--------------------------------------------±----------------------------------------------------------------+$ R7 \* `- |3 h W9 A2 s3 N0 t
| equ256(x:u256, y:u256) -> z:bool | 若 x == y 为 true, 否则为 false |
±--------------------------------------------±----------------------------------------------------------------+
| iszerou256(x:u256) -> z:bool | 若 x == 0 为 true, 否则为 false |; u, `) h! p8 Q
±--------------------------------------------±----------------------------------------------------------------+
| notu256(x:u256) -> z:u256 | ~x, 对 x 按位非 |9 C# H( q1 z+ o' k) s- F2 p7 _% r
±--------------------------------------------±----------------------------------------------------------------+4 [# P( x$ w6 t v3 u8 s
| andu256(x:u256, y:u256) -> z:u256 | x 和 y 按位与 |
±--------------------------------------------±----------------------------------------------------------------+$ h! u) _! |; R( C$ R
| oru256(x:u256, y:u256) -> z:u256 | x 和 y 按位或 |
±--------------------------------------------±----------------------------------------------------------------+
| xoru256(x:u256, y:u256) -> z:u256 | x 和 y 按位异或 |+ z$ i! I" r+ I7 n7 d
±--------------------------------------------±----------------------------------------------------------------+) s: y/ d: G9 p% V$ M5 b2 F
| shlu256(x:u256, y:u256) -> z:u256 | 将 x 逻辑左移 y 位 |
±--------------------------------------------±----------------------------------------------------------------+% Y$ u+ ^) f, `& H+ V
| shru256(x:u256, y:u256) -> z:u256 | 将 x 逻辑右移 y 位 |* p" F' a& i8 W% A; W Q6 e
±--------------------------------------------±----------------------------------------------------------------+& q/ O/ e" ^! W2 V% P
| saru256(x:u256, y:u256) -> z:u256 | 将 x 算术右移 y 位 |8 R. G8 R& C9 r/ Y
±--------------------------------------------±----------------------------------------------------------------+
| byte(n:u256, x:u256) -> v:u256 | x 的第 n 字节,这里的索引位置是从 0 开始的; |; N/ c3 e) I% Z
| | 能否用 and256(shr256(n, x), 0xff) 来替换它, |) j/ v4 R- |1 y
| | 并使它在 EVM 后端之外被优化呢? |2 Y4 F% K8 C# r) U/ G4 V0 b
±--------------------------------------------±----------------------------------------------------------------+; ^5 w8 h( i; e- C9 L) N
| 内存和存储 |
±--------------------------------------------±----------------------------------------------------------------+5 a1 \; P2 A/ d- m
| mload(p:u256) -> v:u256 | mem[p…(p+32)) |
±--------------------------------------------±----------------------------------------------------------------+
| mstore(p:u256, v:u256) | mem[p…(p+32)) := v |6 `4 x7 g; @$ ?3 e Z* J$ W
±--------------------------------------------±----------------------------------------------------------------+
| mstore8(p:u256, v:u256) | mem := v & 0xff - 仅修改单个字节 |2 x/ F+ |3 i; b3 G( k6 d, F* L
±--------------------------------------------±----------------------------------------------------------------+4 s0 W; k( ^; `. P# p
| sload(p:u256) -> v:u256 | storage |
±--------------------------------------------±----------------------------------------------------------------++ L- Y$ u/ {( M( |# c
| sstore(p:u256, v:u256) | storage := v |" ?2 }! ]( `( W
±--------------------------------------------±----------------------------------------------------------------+5 y0 J3 C9 x0 Z1 ~( E( b) i- Y0 o$ Z, Y
| msize() -> size:u256 | 内存的大小, 即已访问过的内存的最大下标, |
| | 因为内存扩展的限制(只能按字进行扩展) |$ a/ z& g6 }+ V3 P
| | 返回值永远都是 32 字节的倍数 |
±--------------------------------------------±----------------------------------------------------------------+
| 执行控制 |8 Z+ e# W! {9 s: W4 q+ W5 }
±--------------------------------------------±----------------------------------------------------------------+$ K/ t6 z6 F- F" w# [
| create(v:u256, p:u256, s:u256) | 以 mem[p…(p+s)) 上的代码创建一个新合约,发送 |
| | v 个 wei,并返回一个新的地址 |9 Z( X) z7 @6 C" H; [) ^
±--------------------------------------------±----------------------------------------------------------------+3 [3 a4 i# y/ R( Q7 _8 o
| 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 用光 |6 x( H$ S8 a3 q- C4 a4 T
| -> r:u256 | 成功,返回 1 |8 A$ S: x9 A3 L4 q7 J6 [
±--------------------------------------------±----------------------------------------------------------------+
| callcode(g:u256, a:u256, v:u256, in:u256, | 相当于 call 但仅仅使用地址 a 上的代码, |
| insize:u256, out:u256, | 而留在当前合约的上下文当中 |
| outsize:u256) -> r:u256 | |! k/ K0 e, E; H/ p0 {$ k
±--------------------------------------------±----------------------------------------------------------------+! j* ^' W* ?; N0 I
| delegatecall(g:u256, a:u256, in:u256, | 相当于 callcode, |
| insize:u256, out:u256, | 但同时保留 caller |
| outsize:u256) -> r:u256 | 和 callvalue |. E3 q: H* N* B9 _
±--------------------------------------------±----------------------------------------------------------------+
| abort() | 终止 (相当于EVM上的非法指令) |4 n% O$ @8 `% l! i6 w
±--------------------------------------------±----------------------------------------------------------------+
| return(p:u256, s:u256) | 终止执行,返回 mem[p…(p+s)) 上的数据 |4 W* |+ |5 s' N% N
±--------------------------------------------±----------------------------------------------------------------+
| revert(p:u256, s:u256) | 终止执行,恢复状态变更,返回 mem[p…(p+s)) 上的数据 |6 u, s/ O4 u/ R" s) o( I3 I8 ]# e
±--------------------------------------------±----------------------------------------------------------------++ h" `9 ?9 I- i! q9 b* v/ C7 Q7 P" F
| selfdestruct(a:u256) | 终止执行,销毁当前合约,并且将余额发送到地址 a |
±--------------------------------------------±----------------------------------------------------------------+; r/ q- Z! f: ]
| log0(p:u256, s:u256) | 用 mem[p…(p+s)] 上的数据产生日志,但没有 topic |
±--------------------------------------------±----------------------------------------------------------------+7 F/ S9 s2 P7 i6 j0 X3 a
| log1(p:u256, s:u256, t1:u256) | 用 mem[p…(p+s)] 上的数据和 topic t1 产生日志 |' R2 [5 B1 s! \9 W. L, C% j) x
±--------------------------------------------±----------------------------------------------------------------+
| log2(p:u256, s:u256, t1:u256, t2:u256) | 用 mem[p…(p+s)] 上的数据和 topic t1,t2 产生日志 |* T& \# [. g, V) \: N0 v$ F
±--------------------------------------------±----------------------------------------------------------------+
| log3(p:u256, s:u256, t1:u256, t2:u256, | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3 产生日志 |
| t3:u256) | |
±--------------------------------------------±----------------------------------------------------------------+% U+ i( Q3 c$ H; C1 Q8 G
| log4(p:u256, s:u256, t1:u256, t2:u256, | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3,t4 |1 G) s9 k4 q' V2 b0 x8 j
| t3:u256, t4:u256) | 产生日志 |
±--------------------------------------------±----------------------------------------------------------------+
| 状态查询 |- w Y2 [. M7 G% x4 A
±--------------------------------------------±----------------------------------------------------------------+) w5 m2 q# M. D& Y* H' n
| blockcoinbase() -> address:u256 | 当前的矿工 |
±--------------------------------------------±----------------------------------------------------------------+
| blockdifficulty() -> difficulty:u256 | 当前区块的难度 |
±--------------------------------------------±----------------------------------------------------------------+
| blockgaslimit() -> limit:u256 | 当前区块的区块 gas 限制 |0 G. C' V S* v
±--------------------------------------------±----------------------------------------------------------------+6 t: W; c7 E/ y; f5 O2 p) E
| blockhash(b:u256) -> hash:u256 | 区块号为 b 的区块的哈希, |
| | 仅可用于最近的 256 个区块,不包含当前区块 |
±--------------------------------------------±----------------------------------------------------------------+
| blocknumber() -> block:u256 | 当前区块号 |; S( Z$ i ~) Q* H; y
±--------------------------------------------±----------------------------------------------------------------+/ @7 @8 B5 u. h+ \1 e
| blocktimestamp() -> timestamp:u256 | 自 epoch 开始的,当前块的时间戳,以秒为单位 |8 e& P& x' _, O; W6 Q
±--------------------------------------------±----------------------------------------------------------------+
| txorigin() -> address:u256 | 交易的发送方 |1 B E. x6 w x! r9 E8 y/ z" O
±--------------------------------------------±----------------------------------------------------------------+( h( \9 z+ Q; t: ^# F' h! J9 F! M
| txgasprice() -> price:u256 | 交易中的 gas 价格 |+ s7 W7 ] f% [+ u _$ ?" l9 Y$ x' }- ^
±--------------------------------------------±----------------------------------------------------------------++ y% X1 F1 X1 D
| gasleft() -> gas:u256 | 还可用于执行的 gas |) ^; g4 X% }7 V9 j( x d
±--------------------------------------------±----------------------------------------------------------------+
| balance(a:u256) -> v:u256 | 地址 a 上的 wei 余额 |" D" } f0 T+ u" O" _
±--------------------------------------------±----------------------------------------------------------------+
| this() -> address:u256 | 当前合约/执行上下文的地址 |
±--------------------------------------------±----------------------------------------------------------------+ X7 A/ M3 E3 s4 p: S+ ?4 R' z
| caller() -> address:u256 | 调用的发送方 (不包含委托调用) |
±--------------------------------------------±----------------------------------------------------------------+
| callvalue() -> v:u256 | 与当前调用一起发送的 wei |9 r4 l' E% X0 H5 j
±--------------------------------------------±----------------------------------------------------------------+" \) U5 J+ f8 H1 ]( O
| calldataload(p:u256) -> v:u256 | 从 position p 开始的 calldata (32 字节) |' O3 f4 ~. G1 w1 B' c! C
±--------------------------------------------±----------------------------------------------------------------+
| calldatasize() -> v:u256 | 以字节为单位的 calldata 的大小 |+ @5 m6 x( b# c0 \8 s
±--------------------------------------------±----------------------------------------------------------------+
| calldatacopy(t:u256, f:u256, s:u256) | 从位置为 f 的 calldata 中,拷贝 s 字节到内存位置 t |
±--------------------------------------------±----------------------------------------------------------------+6 @- a8 z6 J6 @' q: S$ I% y5 @
| codesize() -> size:u256 | 当前合约/执行上下文的代码大小 |
±--------------------------------------------±----------------------------------------------------------------+
| codecopy(t:u256, f:u256, s:u256) | 从 code 位置 f 拷贝 s 字节到内存位置 t |# y' P/ R8 F' f; G
±--------------------------------------------±----------------------------------------------------------------+ z! g" \, \! D; m" q
| extcodesize(a:u256) -> size:u256 | 地址 a 上的代码大小 |8 s0 O5 d5 O( R
±--------------------------------------------±----------------------------------------------------------------+
| extcodecopy(a:u256, t:u256, f:u256, s:u256) | 相当于 codecopy(t, f, s),但从地址 a 获取代码 |! j9 x* s" r' W3 M
±--------------------------------------------±----------------------------------------------------------------+
| 其他 |
±--------------------------------------------±----------------------------------------------------------------+
| discard(unused:bool) | 丢弃值 |. Q# Q: [6 @! e3 {5 C0 C) @4 V
±--------------------------------------------±----------------------------------------------------------------+
| discardu256(unused:u256) | 丢弃值 |: J+ |% [$ u3 s0 ~, o8 L$ z9 y
±--------------------------------------------±----------------------------------------------------------------+0 s" y0 y* j" F2 A
| splitu256tou64(x:u256) -> (x1:u64, x2:u64, | 将一个 u256 拆分为四个 u64 |% I* ]5 b S1 L2 _% R$ Z
| x3:u64, x4:u64) | |, p" N2 M7 `3 B
±--------------------------------------------±----------------------------------------------------------------+0 v. J0 |3 D/ N. Q) S
| combineu64tou256(x1:u64, x2:u64, x3:u64, | 将四个 u64 组合为一个 u256 |8 ]; [% M% N! H; E0 w3 f
| x4:u64) -> (x:u256) | |$ B) L- f0 \, M. H$ |1 N
±--------------------------------------------±----------------------------------------------------------------+/ {, _" i8 B& t5 |) q1 J' `( r1 N
| keccak256(p:u256, s:u256) -> v:u256 | keccak(mem[p…(p+s))) |& f2 a& g3 `! v2 D5 Y* S: k
±--------------------------------------------±----------------------------------------------------------------+
后端
后端或目标负责将 Yul 翻译到特定字节码。 每个后端都可以暴露以后端名称为前缀的函数。 我们为两个建议的后端保留 evm_ 和 ewasm_ 前缀。$ r7 n0 K y# V, G
后端: EVM
目标 |evm| 将具有所有用 evm_ 前缀暴露的 |evm| 底层操作码。' a( W- M% J. I6 d+ B& ^+ w' @
后端: “EVM 1.5”
TBD
后端: eWASM0 @# i+ X9 O% x! q* l5 e# H- `
TBD
Yul 对象说明
语法::
顶层对象 = 'object' '{' 代码? ( 对象 | 数据 )* '}'- ?$ y5 A I( _/ n4 T
对象 = 'object' 字符串字面量 '{' 代码? ( 对象 | 数据 )* '}'9 a( \9 a5 d* W2 c
代码 = 'code' 代码块( | d# }* s3 f7 g3 j0 \; a
数据 = 'data' 字符串字面量 十六进制字面量2 ^* j& O8 L* j6 M; E$ x
十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'
在上面,代码块 指的是前一章中解释的 Yul 代码语法中的 代码块。; j. e* j3 |; A0 y1 Y' V# | C# o
Yul 对象示例如下:
…code::
// 代码由单个对象组成。 单个 “code” 节点是对象的代码。
// 每个(其他)命名的对象或数据部分都被序列化
// 并可供特殊内置函数:datacopy / dataoffset / datasize 用于访问0 w+ l3 ~- [9 D- Y* a+ {
object {
code {3 s q( c$ e1 O: r
let size = datasize("runtime")
let offset = allocate(size)
// 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy( G8 i: t- l9 R/ c9 c
datacopy(dataoffset("runtime"), offset, size)
// 这是一个构造函数,并且运行时代码会被返回
return(offset, size)
}# k$ g5 E1 n' H k O) G+ u# D
data "Table2" hex"4123"' H5 U" C2 T* |+ x, J
object "runtime" {1 n( b Q" |" Q! F5 \
code {
// 运行时代码4 P* r" k6 ?( K5 p: u
let size = datasize("Contract2")
let offset = allocate(size)
// 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy
datacopy(dataoffset("Contract2"), offset, size)
// 构造函数参数是一个数字 0x1234
mstore(add(offset, size), 0x1234)
create(offset, add(size, 32))
}
// 内嵌对象。使用场景是,外层是一个工厂合约,而 Contract2 将是由工厂生成的代码+ S% S5 |# \: r" B" F; g/ c7 L& n
object "Contract2" {/ [' r, q$ {" H8 e I8 ~
code {' C2 ]2 F2 z- Y. r
// 代码在这 ...: h7 I) o- |3 @2 u A
}+ ^! l/ Q4 \' Q# X/ Z5 w
object "runtime" {
code {8 Q0 d3 y7 J% x
// 代码在这 ...% u7 E- m( m9 j `0 J+ N) {. L7 d2 w
}! W+ `, {, Y0 u Y' m
}: s& T7 [8 E% E9 K4 j- I- l8 ~8 q0 c
data "Table1" hex"4123"
}" {" c: H& b( G2 x) R
} e" Y# O# W9 T8 l z1 U
}