Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Yul 语言说明

一夜雨十年灯潞
472 0 0
Yul (先前被也被称为 JULIA 或 IULIA)是一种可以编译到各种不同后端的中间语言( |evm| 1.0,|evm| 1.5,而 eWASM 也在计划中)。
1 H* K" J0 h  o. u$ a正因为如此,它被设计成为这三种平台的可用的共同标准。' D: h3 {- |9 O+ T8 A0 M) c
它已经可以用于 Solidity 内部的“内联汇编”,并且未来版本的 Solidity 编译器甚至会将 Yul 用作中间语言。 为 Yul 构建高级的优化器阶段也将会很容易。
4 ?- O5 F+ s5 V9 N4 W! o… note::4 [. C# v0 S' h; q$ h
请注意,用于“内联汇编”的书写风格是不带类型的(所有的都是 ``u256``),内置函数与 |evm| 操作码相同。6 q/ t1 A; ?7 q4 A
有关详细信息,请参阅内联汇编文档。/ ~1 I4 }8 u; E
Yul 的核心组件是函数,代码块,变量,字面量,for 循环,if 条件语句,switch 条件语句,表达式和变量赋值。$ V- Q2 g, q( Y7 T$ X
Yul 是强类型的,变量和字面量都需要通过前缀符号来指明类型。支持的类型有:bool, u8, s8, u32, s32,+ u% X( F) }; [* ~
u64, s64, u128, s128, u256 和 s256。/ ]9 K- r1 W. R
Yul 本身甚至不提供操作符。如果目标平台是 |evm|,则操作码将作为内置函数提供,但如果后端平台发生了变化,则可以重新实现它们。' _# z. q* v. [% d' Q' e
有关强制性的内置函数的列表,请参阅下面的章节。
3 t- O5 J, `, {以下示例程序假定 |evm| 操作码 mul,div 和 mo 是原生支持或可以作为函数用以计算指数的。
4 H0 `2 [1 ~9 W  N+ p* O2 m# o* j  [… code::  L# ?3 o- W; K( o" P' o( \1 _- [9 `
{
4 B8 a- ^$ e9 C7 S    function power(base:u256, exponent:u256) -> result:u256  K0 q# C* \- v/ n
    {3 J$ J+ ^# I: A+ K
        switch exponent; B1 l' w/ H6 j  H! n
        case 0:u256 { result := 1:u256 }5 u3 a1 t. b9 q/ l
        case 1:u256 { result := base }3 n, V* c! d  C$ w( ~
        default:
( ~! C1 L/ ~) e+ f8 S        {, E2 G% E) D1 }# K$ X* B3 C
            result := power(mul(base, base), div(exponent, 2:u256))
2 s3 q, v, ~6 g            switch mod(exponent, 2:u256)4 |9 d. f( L! R5 n% G) D
                case 1:u256 { result := mul(base, result) }
) d( k6 T: O5 G+ Q$ ^9 ]+ n" c. `4 Y        }2 `( o/ u  v$ M  m6 I6 M
    }: M5 i/ q& v9 `* \" r6 M
}: j$ w. c4 d& w  R% U8 i
也可用 for 循环代替递归来实现相同的功能。这里,我们需要 |evm| 操作码 lt (小于)和 add 可用。* y+ A" Z  H  t# E, V# w
… code::
& ]* c) O# b1 `( m' N6 R{
) o% F6 r+ [% a3 o$ y4 M: n8 m" ?    function power(base:u256, exponent:u256) -> result:u256
/ A  T$ o; f& `& b, T    {
6 l, y- n( R( B0 J        result := 1:u256; I, {  l: c2 |6 ]/ g
        for { let i := 0:u256 } lt(i, exponent) { i := add(i, 1:u256) }9 L0 t* V  O) z) e2 g
        {6 n  |) \' ]/ i3 A! T
            result := mul(result, base)9 ]* q9 z& ^3 q, V- _
        }5 I! O/ I! g. o8 F3 f' s0 Q1 D
    }# F, \; O0 k: j1 M; ^
}- P/ I2 c! c7 j# u% v- y% z
Yul 语言说明3 A0 K: F# s0 S% L* K
本章介绍 Yul 代码。Yul 代码通常放置在一个 Yul 对象中,它将在下一节中介绍。' p: V( K( y3 h/ {
语法::: T% ~6 J: y+ |
代码块 = '{' 语句* '}'
; @3 a! ^  z  O语句 =
" F  }+ N& P6 L4 c& _9 }    代码块 |
( Z: z' k5 ]' j4 G% a8 t5 u) G) t% _    函数定义 |
( W, t" l- ]4 N: q    变量声明 |6 q0 u0 i6 H5 l+ O8 F6 d) r
    赋值 |; I. L2 \% N% P; c5 i' q1 D$ S% `4 {
    表达式 |( F9 J( |5 B  j3 e6 Y7 h- u
    Switch |' \  U% l0 `. y2 a3 `
    For 循环 |& W3 G: x& [8 n6 Q% c' n$ s' e
    循环中断* S( m9 D0 ]- D: F& W
函数定义 =
( q6 w% h$ u; A% k9 |* y    'function' 标识符 '(' 带类型的标识符列表? ')') k2 ]: a+ [; @/ c! t" T
    ( '->' 带类型的标识符列表 )? 代码块
  {7 {  t- \+ N8 s$ ]* m变量声明 =( k' J1 K6 J8 g, B# {$ Q/ ?* Z
    'let' 带类型的标识符列表 ( ':=' 表达式 )?
/ n8 ~" g& n& e2 P0 C. z0 y赋值 =
2 V% P3 x% w  ^+ R' [' o) n    标识符列表 ':=' 表达式2 I* V# T8 _) R* p5 M: t
表达式 =
: ]. u, E; d8 V& p    函数调用 | 标识符 | 字面量7 ?/ T  ]4 M% g* x) }
If 条件语句 =, [7 `: k' }7 w
    'if' 表达式 代码块9 l0 M4 C' S8 O, P  a: u
Switch 条件语句 =
% k4 I# ]0 |; y% ]) v; b- |( G    'switch' 表达式 Case* ( 'default' 代码块 )?% b( r1 M; f/ x) U- S" ?9 I
Case =, a7 o+ P! k# p
    'case' 字面量 代码块+ ^4 h; I0 E- H
For 循环 =
1 Z3 k: ^* ?# I$ K! f" |% w) w    'for' 代码块 表达式 代码块 代码块
0 k8 f8 s  R2 i( A4 u, ~0 A. ?循环中断 =
+ O# x% h* `5 I3 s; K, J3 R3 o    'break' | 'continue'* k! w3 J" p- b
函数调用 =
* ]8 [! O1 u1 t  |8 u8 g    标识符 '(' ( 表达式 ( ',' 表达式 )* )? ')'- H- ^, `/ F8 O8 T' N! ?
标识符 = [a-zA-Z_$] [a-zA-Z_0-9]*
$ b; p4 V9 y7 d! D5 }标识符列表 = 标识符 ( ',' 标识符)** e' C3 _% R2 i. i8 y+ y
类型名 = 标识符 | 内置的类型名3 y. r3 f2 J- V: q
内置的类型名 = 'bool' | [us] ( '8' | '32' | '64' | '128' | '256' )
8 D& J* h8 k" |. w- M- o0 N带类型的标识符列表 = 标识符 ':' 类型名 ( ',' 标识符 ':' 类型名 )*
  L7 m9 l( Z  ?  k- d, {4 V! f5 H字面量 =
3 c- T( O0 K; y$ O6 m* W$ }/ l    (数字字面量 | 字符串字面量 | 十六进制字面量 | True字面量 | False字面量) ':' 类型名
) a0 |0 C2 n; p$ R6 p数字字面量 = 十六进制数字 | 十进制数字
  d9 P8 E3 \; N- q  g7 S: G十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
0 o( g; c8 ]: C! w字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'
( T6 [( Y# s" O( s4 \/ l# L: zTrue字面量 = 'true'
5 @: _* z% E' p) B7 \False字面量 = 'false'6 M7 B# T2 k6 ?: h/ M/ a9 j- m( ?/ @
十六进制数字 = '0x' [0-9a-fA-F]+
: b/ L; S" q& j# w" N十进制数字 = [0-9]+2 r* U. m' [6 {$ p0 e
语法层面的限制  P; E, q& x7 t6 g0 x
Switches 必须至少有一个 case(包括 default )。
' S' N1 k5 J  I6 E) r  l5 w! s) ^如果表达式的所有可能值都被覆盖了,那么不应该允许使用 default
/ z6 P4 e# Q8 R/ k(即带 bool 表达式的 switch 语句同时具有 true case 和 false case 的情况下不应再有 default 语句)。. }! M% q% j( R1 j
每个表达式都求值为零个或多个值。 标识符和字面量求值为一个值,函数调用求值为所调用函数的返回值。% o$ p) \; K/ l4 R6 t. c: Q
在变量声明和赋值中,右侧表达式(如果存在)求值后,必须得出与左侧变量数量相等的值。9 s) g+ s$ d7 Z0 ]3 t, S' ~& |. i2 @
这是唯一允许求值出多个值的表达式。" n$ h3 p, K. G' U& e' K! a
那种同时又是语句的表达式(即在代码块的层次)求值结果必须只有零个值。9 V* o0 J6 r$ P; X; l. g
在其他所有情况中,表达式求值后必须仅有一个值。
2 P$ P4 e" N0 J# V: e6 Ocontinue 和 break 语句只能用在循环体中,并且必须与循环处于同一个函数中(或者两者都必须在顶层)。, ]9 V5 z* u, w& f3 }5 R
for 循环的条件部分的求值结果只能为一个值。0 C5 B6 r% V$ N
字面量不可以大于它们本身的类型。已定义的最大类型宽度为 256 比特。
0 f6 _, x4 D8 T! n作用域规则
% V. s* z6 C" l# I8 r! ~* iYul 中的作用域是与块(除了函数和 for 循环,如下所述)和所有引入新的标识符到作用域中的声明
$ c3 w& I1 v3 t7 [3 X( FunctionDefinition ,VariableDeclaration )紧密绑定的。3 I  `, `6 ]/ e
标识符在将其定义的块中可见(包括所有子节点和子块)。6 Q, e5 {& ]/ s3 z2 |9 l
作为例外,for 循环的 “init” 部分中(第一个块)定义的标识符在 for 循环的所有其他部分(但不在循环之外)中都是可见的。( z$ _" P2 X: I
在 for 循环的其他部分声明的标识符遵守常规的作用域语法规则。: g2 P7 O8 |, K! ^5 w; m: r6 I
函数的参数和返回参数在函数体中可见,并且它们的名称不能相同。+ v( V8 b3 {  W2 ]
变量只能在声明后引用。 尤其是,变量不能在它们自己的变量声明的右边被引用。& a$ {2 n# _& D! d3 c; [7 z
函数可以在声明之前被引用(如果它们是可见的)。, K. v: u0 ^/ n0 `0 R
Shadowing 是不被允许的,即是说,你不能在同名标识符已经可见的情况下又定义该标识符,即使它是不可访问的。
; y' T7 G1 y; w" r4 {2 F+ ]' K在函数内,不可能访问声明在函数外的变量。
- `" s! Y2 c0 d6 L( |- w% Z形式规范
: p0 v/ D7 Z; b, ^我们通过在 AST 的各个节点上提供重载的求值函数 E 来正式指定 Yul。: \7 i& w- X! I0 ?& g0 E" x
任何函数都可能有副作用,所以 E 接受两个状态对象和 AST 节点作为它的参数,并返回两个新的状态对象和数量可变的其他值。# v3 m) x* t& r, F) D4 B: z
这两个状态对象是全局状态对象(在 |evm| 的上下文中是 |memory|,|storage| 和区块链的状态)和本地状态对象(局部变量的状态,即 |evm| 中堆栈的某个段)。
( x! Q2 q8 T) s4 i" U+ h7 Q4 t如果 AST 节点是一个语句,E 将返回两个状态对象和一个用于 break 和 continue 语句的 “mode”。
9 f& J; m. C) L* D" b如果 AST 节点是表达式,则 E 返回两个状态对象,并返回与表达式求值结果相同数量的值。
& Q2 Y8 U+ @$ p% y3 B在这份高层次的描述中,并没有对全局状态的确切本质进行说明。
/ B  H/ p2 L$ T8 m1 N5 l# [9 J3 x  ^本地状态 L 是标识符 i 到值 v 的映射,表示为 L = v。, C* t( F2 W# k* d3 v. s6 ]
对于标识符 v, 我们用 $v 作为标识符的名字。' d- S' a& i0 Z* l
我们将为 AST 节点使用解构符号。% ~! l* X1 C. e: _
… code::
" Q" x6 [+ |9 B% sE(G, L, : Block) =: I2 J7 G6 k  |$ N3 A; k
    let G1, L1, mode = E(G, L, St1, ..., Stn)
- r% L  I/ B9 W2 _8 ?    let L2 be a restriction of L1 to the identifiers of L9 Z1 }' R3 d" E& S
    G1, L2, mode' q* n5 C9 J9 j9 x- q& i  Q
E(G, L, St1, ..., Stn: Statement) =5 A7 Q6 n+ R2 w' v
    if n is zero:' a! t; X  H2 W% S
        G, L, regular
0 h: }! m5 J5 _! u    else:: K# C% y+ ^! n1 h; C- L/ N# t2 ]
        let G1, L1, mode = E(G, L, St1)
7 i- D$ _* ?( n; F2 V' u        if mode is regular then) i5 r: W0 ]( R# S0 h% {6 `
            E(G1, L1, St2, ..., Stn)6 X  q; O* O! j. i( `4 Z. q
        otherwise, X! v$ m" _. D& Y6 x
            G1, L1, mode
8 S0 m3 P* {' {! L3 w% S% ]E(G, L, FunctionDefinition) =
- t5 H3 e! p" b& F7 s    G, L, regular
, a$ e* B8 j2 ]( [! J1 k! wE(G, L, : VariableDeclaration) =
8 b' z1 F+ L1 Y    E(G, L, : Assignment)
/ u0 O5 I1 s: @4 JE(G, L, : VariableDeclaration) =1 u8 K* {5 c9 O# ?4 [$ p7 u# O
    let L1 be a copy of L where L1[$vari] = 0 for i = 1, ..., n
6 o, Z# {% K) v7 C    G, L1, regular9 X  @4 S- @7 Z, c- n  Z$ h' _
E(G, L, : Assignment) =
3 l/ K+ n& h; [, n    let G1, L1, v1, ..., vn = E(G, L, rhs); o- a% Q2 J1 i5 E6 O
    let L2 be a copy of L1 where L2[$vari] = vi for i = 1, ..., n5 E" x1 _1 G: M- @* c( Q) ]( Z
    G, L2, regular
5 K* P$ E6 C& B! y4 E& RE(G, L, : ForLoop) =- F# ]( y$ C  Y% G% ~: Q# Z
    if n >= 1:( J/ ~6 |; h( I2 a0 A
        let G1, L1, mode = E(G, L, i1, ..., in)
" |/ p3 y* ^" ]        // 由于语法限制,mode 必须是规则的& ?' @- F3 }! L( X; U# o, g  Y. k
        let G2, L2, mode = E(G1, L1, for {} condition post body)
+ h  B& q% R1 z5 I5 j; c$ I        // 由于语法限制,mode 必须是规则的
6 n/ t4 t) }5 ?7 x        let L3 be the restriction of L2 to only variables of L# c+ B& L/ Z2 Z, V' z
        G2, L3, regular
3 V1 A) `9 y9 ]    else:
; T3 E$ j! }4 |0 A1 T- @0 ^        let G1, L1, v = E(G, L, condition)
5 _+ _* r' [8 }8 H        if v is false:- N  _' k3 p" E. d4 ?! j
            G1, L1, regular
; V! F, ?. `+ p; u( m! h3 ^; W        else:  Z' Q7 n  v+ f' }1 ]
            let G2, L2, mode = E(G1, L, body), i. I% G4 S+ M, u/ B8 M
            if mode is break:
. p; L5 _; m+ A" g: o                G2, L2, regular% p& Z: f6 q+ r0 Z  }4 s
            else:
+ q8 T1 z# W2 U; ^+ ]                G3, L3, mode = E(G2, L2, post)
8 f' w, n- G! O: ]$ Y) c0 J- M3 i5 v                E(G3, L3, for {} condition post body)
' |* s. C, j# q; E6 P. Z; i/ i4 ME(G, L, break: BreakContinue) =. K/ F' }2 \# f" Z3 x& i
    G, L, break* p3 \2 [5 e% v. \& f
E(G, L, continue: BreakContinue) =9 c" }' x. C" n" a
    G, L, continue
1 S% R& k, Y$ cE(G, L, : If) =: v$ D& B/ p$ S7 `" M) D
    let G0, L0, v = E(G, L, condition)9 b- |% ~) R" N) N
    if v is true:
. l) A( I6 E% |% O5 f        E(G0, L0, body)0 W' @7 X0 K$ L( q$ q! s* a
    else:8 ]& W, R% t9 h6 D" C! `
        G0, L0, regular
* z( r! l! T9 {E(G, L, : Switch) =
: x, b) L" A' K: F2 n    E(G, L, switch condition case l1:t1 st1 ... case ln:tn stn default {})
' N9 E* }8 B3 z+ l0 K1 R# jE(G, L, : Switch) =
' v8 M2 I4 I8 k    let G0, L0, v = E(G, L, condition)
" k/ P1 F2 m: X) b3 C    // i = 1 .. n$ ~1 E/ ?1 p  ~/ z& O* N& [2 z
    // 对字面量求值,上下文无关9 h9 ~- ?9 z9 W" A1 \
    let _, _, v1 = E(G0, L0, l1)) x* I0 C! A/ e) v- }* B: b
    ...3 y- Y  f* d1 ~; V1 j
    let _, _, vn = E(G0, L0, ln)
  G1 R) E" E7 l9 }) B" T    if there exists smallest i such that vi = v:+ \) @' m: |/ A5 a5 H
        E(G0, L0, sti)* t  f$ o/ e( b' u0 ^
    else:
1 D0 q# U+ \. X$ q, C7 ^        E(G0, L0, st')  O" b" e- l6 f2 U
E(G, L, : Identifier) =
3 P, Y  T5 c4 t& Z  m7 q8 F    G, L, L[$name]4 |- F. F1 f% t0 d7 B9 L) @
E(G, L, : FunctionCall) =
9 u( z8 d4 ?4 B    G1, L1, vn = E(G, L, argn)$ D2 l2 Q8 F1 ]2 ~; t
    ...
& y( J: A  J: f. ^5 a    G(n-1), L(n-1), v2 = E(G(n-2), L(n-2), arg2)3 f8 T0 Q. d, K* k2 M: L
    Gn, Ln, v1 = E(G(n-1), L(n-1), arg1)
) U5 O. Z% A! _' a: @. A/ y    Let  ret1, ..., retm block>
4 Z# j  x, t# r& t- [# r2 W    be the function of name $fname visible at the point of the call./ s% i4 H' n) b1 N6 W+ }% `, R
    Let L' be a new local state such that
& I+ M3 f; h1 _    L'[$parami] = vi and L'[$reti] = 0 for all i.
" l  u3 q; E( |2 ~; `0 [+ A/ D    Let G'', L'', mode = E(Gn, L', block)4 x" L* F) G' h6 C( J# [5 ?
    G'', Ln, L''[$ret1], ..., L''[$retm]
; R/ ^/ y" U  P. SE(G, L, l: HexLiteral) = G, L, hexString(l),+ L7 g1 q  k' q. E2 b9 b4 N
    where hexString decodes l from hex and left-aligns it into 32 bytes. D1 O6 {6 z, ?% q+ N
E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l),3 k% @3 R. Z/ R$ J5 P
    where utf8EncodeLeftAligned performs a utf8 encoding of l$ h4 V% I* d" V/ N" \* J0 z2 q# ^
    and aligns it left into 32 bytes. Z5 I9 P7 M' W; C5 V
E(G, L, n: HexNumber) = G, L, hex(n)
- v8 y* z. ]: F    where hex is the hexadecimal decoding function) P: j) P3 v! e: }* O& Q
E(G, L, n: DecimalNumber) = G, L, dec(n),
7 w& f& X! W: k; g    where dec is the decimal decoding function
5 s. [* w- X* e) e- B: C, `类型转换函数- B* n% ^( L0 S. ~2 C" j' L
Yul 不支持隐式类型转换,因此存在提供显式转换的函数。
' o) p, n, y4 M; N  t( r在将较大类型转换为较短类型时,如果发生溢出,则可能会发生运行时异常。
, n0 w0 ~( X! A4 _2 A下列类型的“截取式”转换是允许的:0 J1 H, x3 S- i* h4 M. z! c) f
  • bool
  • u32
  • u64
  • u256
  • s2562 X& O" s% o7 `9 {
    . r' x6 n2 V0 j/ h
    这里的每种类型的转换函数都有一个格式为 to(x:) -> y: 的原型,
    ! S# e1 v' T0 r. C5 J) L/ j/ s比如 u32tobool(x:u32) -> y:bool、u256tou32(x:u256) -> y:u32 或 s256tou256(x:s256) -> y:u256。
    ' ~! @  I7 o! v- q7 u( t* r3 _… note::
    6 ^1 W$ D# b: `0 `" R``u32tobool(x:u32) -> y:bool`` 可以由 ``y := not(iszerou256(x))`` 实现,并且
    - I- \$ Y% k. @. ?/ R2 |1 b( C``booltou32(x:bool) -> y:u32`` 可以由 ``switch x case true:bool { y := 1:u32 } case false:bool { y := 0:u32 }`` 实现
    * V1 ]3 m5 ^4 r& {' M3 N低级函数
    ; O% `  _: ]; N7 ~' ]; N" s3 H) q以下函数必须可用:
    5 V4 k% n( K: O1 \+ n, O  k( ^±--------------------------------------------------------------------------------------------------------------+, Y1 T. e% ~1 v2 \/ O' |
    | 逻辑操作                                                                                                    |
    # k: _5 g: d( I* [8 B$ Q' e±--------------------------------------------±----------------------------------------------------------------+
    + P1 a# d& k) ~2 A! L3 [" ]| not(x:bool) -> z:bool                       | 逻辑非                                                          |
    ) [% m4 Z" @3 ~' T±--------------------------------------------±----------------------------------------------------------------+
    , ^( O6 g( C- i' q# r| and(x:bool, y:bool) -> z:bool               | 逻辑与                                                          |
    0 h) A9 C% o$ x- Q5 f) X±--------------------------------------------±----------------------------------------------------------------+, g* k+ K) C  M! T$ n. O7 Q
    | or(x:bool, y:bool) -> z:bool                | 逻辑或                                                          |
    8 n- N! _3 c1 Y- V; q; U5 g  @±--------------------------------------------±----------------------------------------------------------------+0 E4 F5 a; u( s( l
    | xor(x:bool, y:bool) -> z:bool               | 异或                                                            |
    8 @' R- h, }1 I; w# `±--------------------------------------------±----------------------------------------------------------------+
    : f" w; E' i5 Y- q, P/ d" x| 算术操作                                                                                                    |
    9 u% T3 C# P( i! N7 H/ }±--------------------------------------------±----------------------------------------------------------------+  e: q4 p( v! R0 H
    | addu256(x:u256, y:u256) -> z:u256           | x + y                                                           |
    6 U1 Y; {( G& ?6 D% G±--------------------------------------------±----------------------------------------------------------------+
    . @6 C/ `( H; o2 @/ b# f| subu256(x:u256, y:u256) -> z:u256           | x - y                                                           |1 `7 h- t0 X, i' D( m/ p% j
    ±--------------------------------------------±----------------------------------------------------------------+
    ' s2 a( |: D1 Q! ?2 ?| mulu256(x:u256, y:u256) -> z:u256           | x * y                                                           |
    : k, u1 N& z  s9 F0 v  f$ e. I±--------------------------------------------±----------------------------------------------------------------+/ B' {. T) m0 w2 i& b& x
    | divu256(x:u256, y:u256) -> z:u256           | x / y                                                           |; _9 q3 K7 f: T
    ±--------------------------------------------±----------------------------------------------------------------+& |( f% q3 j+ R, }: G' r; S" H
    | divs256(x:s256, y:s256) -> z:s256           | x / y, 有符号数用补码形式                                       |# Z. ~% U- h9 Q+ g1 x
    ±--------------------------------------------±----------------------------------------------------------------+& L; l  S: j2 w4 X/ Q# z
    | modu256(x:u256, y:u256) -> z:u256           | x % y                                                           |
    / F7 c, ^' O" g4 _/ w4 `±--------------------------------------------±----------------------------------------------------------------+% Y+ g  {0 J# ^! C9 y' Z' q' g
    | mods256(x:s256, y:s256) -> z:s256           | x % y, 有符号数用补码形式                                       |0 s, T: {4 f; w/ F  B+ ]- _
    ±--------------------------------------------±----------------------------------------------------------------+3 Q4 e+ q9 w7 c$ O9 a
    | signextendu256(i:u256, x:u256) -> z:u256    | 从第 (i*8+7) 位开始进行符号扩展,从最低符号位开始计算           |) P% S- s- R, o
    ±--------------------------------------------±----------------------------------------------------------------+
    $ n& a; K; w8 s1 c: j0 y* R| expu256(x:u256, y:u256) -> z:u256           | x 的 y 次方                                                     |
    7 A  {: Z* }3 c±--------------------------------------------±----------------------------------------------------------------+
    + Z8 {! m/ G) d4 M| addmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x + y) % m                                |
    2 H& z: F3 l2 A- C7 I±--------------------------------------------±----------------------------------------------------------------+
    9 t" p4 }' r2 |* h& {- w| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x * y) % m                                |0 j* r& t% o! N3 }
    ±--------------------------------------------±----------------------------------------------------------------+" a0 g- e3 ]8 L3 ^7 c
    | ltu256(x:u256, y:u256) -> z:bool            | 若 x  z:bool            | 若 x > y 为 true, 否则为 false                                  |
    * k7 o" s& c+ ?$ [4 m9 l( y±--------------------------------------------±----------------------------------------------------------------+
    + S+ ?) @: f# d- k9 P( ~| sltu256(x:s256, y:s256) -> z:bool           | 若 x  z:bool           | 若 x > y 为 true, 否则为 false                                  |2 v" O1 F0 Y; ^3 q8 O+ i
    |                                             | 有符号数用补码形式                                              |! x8 K- P  L0 X7 L: O( P) H
    ±--------------------------------------------±----------------------------------------------------------------+! a) x. u& b. P6 F
    | equ256(x:u256, y:u256) -> z:bool            | 若 x == y 为 true, 否则为 false                                 |; a& i. l2 a7 E, n' R' z
    ±--------------------------------------------±----------------------------------------------------------------+
    7 s: Y& L/ G  d9 Q( Q1 A" `| iszerou256(x:u256) -> z:bool                | 若 x == 0 为 true, 否则为 false                                 |% [+ k/ t" s, d8 o
    ±--------------------------------------------±----------------------------------------------------------------+
    ; [9 Z  p' W+ k7 P; [! m$ Y| notu256(x:u256) -> z:u256                   | ~x, 对 x 按位非                                                 |) [% \2 P8 U6 i
    ±--------------------------------------------±----------------------------------------------------------------+
    1 g: J0 l. c, p5 X| andu256(x:u256, y:u256) -> z:u256           | x 和 y 按位与                                                   |
    6 u, ~/ ^7 V, L# ~& [) J. F±--------------------------------------------±----------------------------------------------------------------+
    , H9 E1 |; i! y" y* s0 z$ p| oru256(x:u256, y:u256) -> z:u256            | x 和 y 按位或                                                   |5 ]' [0 f7 g/ i0 n* y5 w
    ±--------------------------------------------±----------------------------------------------------------------+
    - E6 |/ u3 e- E/ }4 [. S2 q| xoru256(x:u256, y:u256) -> z:u256           | x 和 y 按位异或                                                 |, {8 g' _1 {' |+ |& }+ `' y( Z5 S
    ±--------------------------------------------±----------------------------------------------------------------+
    2 Q6 h( Y1 A' s/ E| shlu256(x:u256, y:u256) -> z:u256           | 将 x 逻辑左移 y 位                                              |
      t( Y7 a1 Z9 F5 l1 R±--------------------------------------------±----------------------------------------------------------------+2 F* C4 x3 M) p. A% P4 f9 Q3 T7 q' [
    | shru256(x:u256, y:u256) -> z:u256           | 将 x 逻辑右移 y 位                                              |
    7 O4 w. Y, ?( J+ V  z( X3 _3 R±--------------------------------------------±----------------------------------------------------------------+
    4 a% T: ^2 |* z- ~& j( f6 {- q| saru256(x:u256, y:u256) -> z:u256           | 将 x 算术右移 y 位                                              |& U& r$ ~; g# J5 l2 W+ H0 w1 x0 x. l
    ±--------------------------------------------±----------------------------------------------------------------+
    $ |8 c8 X# O% c, Y# a| byte(n:u256, x:u256) -> v:u256              | x 的第 n 字节,这里的索引位置是从 0 开始的;                    |
    . d- C, m* x. W' Q' P! X. [|                                             | 能否用 and256(shr256(n, x), 0xff) 来替换它,                    |$ Q4 |/ R* Q4 s4 i$ ~" G
    |                                             | 并使它在 EVM 后端之外被优化呢?                                 |) o* {6 [$ N+ R5 F0 B. Z2 ^. b) ~
    ±--------------------------------------------±----------------------------------------------------------------+
    # i( ?& o( j5 e# s| 内存和存储                                                                                                  |
    2 g, @4 z* P( q/ y" Y* _±--------------------------------------------±----------------------------------------------------------------+
    1 Q  L: j2 ~0 ~" v' r8 `| mload(p:u256) -> v:u256                     | mem[p…(p+32))                                                  |! P: ?9 f' J' ~4 W: s9 X4 T* J
    ±--------------------------------------------±----------------------------------------------------------------+
    ( W. }0 u6 S# v" g! t| mstore(p:u256, v:u256)                      | mem[p…(p+32)) := v                                             |  U! {! W, m3 W) n0 [0 o
    ±--------------------------------------------±----------------------------------------------------------------+
    " a' z: x1 J9 B# h. ], p) \; s| mstore8(p:u256, v:u256)                     | mem := v & 0xff    - 仅修改单个字节                          |  J# a6 x/ k' ~, q- V0 z( H
    ±--------------------------------------------±----------------------------------------------------------------+! V& q( @% I9 Y3 q
    | sload(p:u256) -> v:u256                     | storage                                                      |* Q# X- W8 K/ h0 G
    ±--------------------------------------------±----------------------------------------------------------------+( L) e$ y( n# @, i% m7 D" E
    | sstore(p:u256, v:u256)                      | storage := v                                                 |* ]( Z4 |- W7 A) P- y. V1 ^( C
    ±--------------------------------------------±----------------------------------------------------------------+
    & @* t8 E8 y/ c* B| msize() -> size:u256                        | 内存的大小, 即已访问过的内存的最大下标,                        |
    ; S# E+ e) v2 A+ c|                                             | 因为内存扩展的限制(只能按字进行扩展)                          |( z5 ], s- j/ S9 \& o
    |                                             | 返回值永远都是 32 字节的倍数                                    |: u2 g  ?& X% @  C6 }
    ±--------------------------------------------±----------------------------------------------------------------+
    7 `& K# ^/ q4 W/ ~! k0 H" m- k# K| 执行控制                                                                                                    |
    2 p0 b& F8 D4 k* y3 U" @±--------------------------------------------±----------------------------------------------------------------+
    % x" i' z- K8 V2 `| create(v:u256, p:u256, s:u256)              | 以 mem[p…(p+s)) 上的代码创建一个新合约,发送                   |
    , x) r9 [, b. X# l|                                             | v 个 wei,并返回一个新的地址                                    |
    ' I8 z5 `3 E* R" a: |: x±--------------------------------------------±----------------------------------------------------------------+1 C7 ~" _) h7 b0 g
    | call(g:u256, a:u256, v:u256, in:u256,       | 调用地址 a 上的合约,以 mem[in…(in+insize)) 作为输入           |
    2 X) n1 _( o/ w8 ]. S) I| insize:u256, out:u256,                      | 一并发送 g gas 和 v wei ,以 mem[out…(out+outsize))            |
    9 c6 a# `+ }2 x" A| outsize:u256)                               | 作为输出空间。若错误,返回 0 (比如,gas 用光                   |2 `" n9 j/ e# v7 v' M
    | -> r:u256                                   | 成功,返回 1                                                    |7 K5 b. `/ {2 x" I4 ~9 h1 {5 K" J9 t
    ±--------------------------------------------±----------------------------------------------------------------+( ]) M' P8 e  x
    | callcode(g:u256, a:u256, v:u256, in:u256,   | 相当于 call 但仅仅使用地址 a 上的代码,                     |
    , Q, p' z( B6 V- o4 S| insize:u256, out:u256,                      | 而留在当前合约的上下文当中                                      |
      s; E1 R& k- J9 h5 [  Z9 S3 C| outsize:u256) -> r:u256                     |                                                                 |, @4 y1 W. f, B# H, {7 z1 c0 s
    ±--------------------------------------------±----------------------------------------------------------------+# _5 ?) s2 U: _' u4 m
    | delegatecall(g:u256, a:u256, in:u256,       | 相当于 callcode,                                           |* P1 z& v; G5 F1 Z! Y
    | insize:u256, out:u256,                      | 但同时保留 caller                                           |
    ) J0 a/ A) V. o  W5 Y| outsize:u256) -> r:u256                     | 和 callvalue                                                |
    " E) b8 L) V5 q. z2 b7 w! o±--------------------------------------------±----------------------------------------------------------------+
    % {  X+ R$ M" ^( G* v" r, a| abort()                                     | 终止 (相当于EVM上的非法指令)                                    |
    5 W7 Z% X/ z: J±--------------------------------------------±----------------------------------------------------------------+9 I. U2 ?7 Y" j5 {/ P
    | return(p:u256, s:u256)                      | 终止执行,返回 mem[p…(p+s)) 上的数据                           |
    4 C! E# N6 A) O1 S% Q3 L( F±--------------------------------------------±----------------------------------------------------------------+# S0 Y4 c, ?8 x4 N; J
    | revert(p:u256, s:u256)                      | 终止执行,恢复状态变更,返回 mem[p…(p+s)) 上的数据             |
    5 F+ w5 y) e3 H; c3 R±--------------------------------------------±----------------------------------------------------------------+
    + h% s+ T, a# k3 t2 f0 L: Z  v| selfdestruct(a:u256)                        | 终止执行,销毁当前合约,并且将余额发送到地址 a                  |
    # ]  A, q; ^' z: A4 q4 H±--------------------------------------------±----------------------------------------------------------------+% |& o3 j. \7 a+ P* Q
    | log0(p:u256, s:u256)                        | 用 mem[p…(p+s)] 上的数据产生日志,但没有 topic                 |
    ; E: S  ]1 L. e+ o" I±--------------------------------------------±----------------------------------------------------------------+/ Z5 _- K2 h/ g/ H& i" `
    | log1(p:u256, s:u256, t1:u256)               | 用 mem[p…(p+s)] 上的数据和 topic t1 产生日志                   |
    0 y  A4 B# b" I, l±--------------------------------------------±----------------------------------------------------------------+& C6 U2 `$ x3 b8 e. v' W0 e
    | log2(p:u256, s:u256, t1:u256, t2:u256)      | 用 mem[p…(p+s)] 上的数据和 topic t1,t2 产生日志               |% ]) M1 a9 z' e/ u* K  E
    ±--------------------------------------------±----------------------------------------------------------------+
    " h' n$ p6 l/ o/ k/ v| log3(p:u256, s:u256, t1:u256, t2:u256,      | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3 产生日志           |
    # t' r: u3 V, [| t3:u256)                                    |                                                                 |
      g1 l9 H3 r& g- }  N7 `9 l±--------------------------------------------±----------------------------------------------------------------+
    6 a; I  q  _" j) T: o" \| log4(p:u256, s:u256, t1:u256, t2:u256,      | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3,t4                |" X# W9 E2 q/ J$ f
    | t3:u256, t4:u256)                           | 产生日志                                                        |
    - R' \  Q, N0 h3 t: S±--------------------------------------------±----------------------------------------------------------------+
    / _& |9 U- Q# e& @( k# E| 状态查询                                                                                                    |$ x1 b6 v# n& ^: ~7 j
    ±--------------------------------------------±----------------------------------------------------------------+
    2 G1 b4 j7 M! p! V0 u" L* R0 r| blockcoinbase() -> address:u256             | 当前的矿工                                                      |! f: L; w* k+ j) Y6 w( Z
    ±--------------------------------------------±----------------------------------------------------------------+8 X" ^! a3 u+ r* Q
    | blockdifficulty() -> difficulty:u256        | 当前区块的难度                                                  |0 |! p2 w! F$ N3 ]% B. u. A. F. ^
    ±--------------------------------------------±----------------------------------------------------------------+8 V" r3 W7 _8 G8 e1 Y/ c
    | blockgaslimit() -> limit:u256               | 当前区块的区块 gas 限制                                         |
    9 x$ o6 x" {# I3 z( H±--------------------------------------------±----------------------------------------------------------------+- U$ P$ K+ J# C9 C
    | blockhash(b:u256) -> hash:u256              | 区块号为 b 的区块的哈希,                                       |' V3 O( s3 j3 N9 [; S
    |                                             | 仅可用于最近的 256 个区块,不包含当前区块                       |
    % ^, q9 F) O/ |1 x. S- o# b2 q±--------------------------------------------±----------------------------------------------------------------+
    - C: g' m' A! H% q$ h| blocknumber() -> block:u256                 | 当前区块号                                                      |% g! B! O' X( H% f+ j
    ±--------------------------------------------±----------------------------------------------------------------+
    , d. s* d7 M* [4 l. P| blocktimestamp() -> timestamp:u256          | 自 epoch 开始的,当前块的时间戳,以秒为单位                     |( K3 U4 {3 B, h8 V' n( o( R$ V) B
    ±--------------------------------------------±----------------------------------------------------------------+6 R! E# z3 v2 h. [" E: w7 c
    | txorigin() -> address:u256                  | 交易的发送方                                                    |8 T7 X* j6 A7 _2 i5 ]) r0 r# ^( i
    ±--------------------------------------------±----------------------------------------------------------------+
    * w0 _0 A7 A, R0 v| txgasprice() -> price:u256                  | 交易中的 gas 价格                                               |
    0 ~- ]/ p  M( n' s" r9 @±--------------------------------------------±----------------------------------------------------------------+
    / d: u( k4 K0 r& B0 |) ^# H1 c1 w| gasleft() -> gas:u256                       | 还可用于执行的 gas                                              |
    8 s  ~$ T( m, l±--------------------------------------------±----------------------------------------------------------------+
    2 \! K1 [' z- M. Z4 d| balance(a:u256) -> v:u256                   | 地址 a 上的 wei 余额                                            |
    ) @% A0 W! v+ R6 {' W' a0 e7 @: x+ {±--------------------------------------------±----------------------------------------------------------------+4 `8 [4 r% B4 I; O% P
    | this() -> address:u256                      | 当前合约/执行上下文的地址                                      |0 X: h  F5 ?& J/ @$ v% F0 i  Q3 k
    ±--------------------------------------------±----------------------------------------------------------------+2 U& T3 x6 b' R- n- l( M) {
    | caller() -> address:u256                    | 调用的发送方 (不包含委托调用)                                   |: ]3 x. g. p$ M" u4 g
    ±--------------------------------------------±----------------------------------------------------------------+
      v5 f; I. p, Z" Y3 h) U| callvalue() -> v:u256                       | 与当前调用一起发送的 wei                                        |
    & y) ]/ J, z! ^! S±--------------------------------------------±----------------------------------------------------------------+- @, Q/ t8 p5 z+ C! @
    | calldataload(p:u256) -> v:u256              | 从 position p 开始的 calldata (32 字节)                         |2 a5 ^4 s6 j! c! w/ r
    ±--------------------------------------------±----------------------------------------------------------------+
    7 h: n/ L8 e+ H" d' A) r2 \| calldatasize() -> v:u256                    | 以字节为单位的 calldata 的大小                                  |
    ) V5 D1 c! W2 o; Z. C±--------------------------------------------±----------------------------------------------------------------+
    ! g, U7 z0 A) }# e; L| calldatacopy(t:u256, f:u256, s:u256)        | 从位置为 f 的 calldata 中,拷贝 s 字节到内存位置 t              |: y9 N$ D4 Y. A% L/ w
    ±--------------------------------------------±----------------------------------------------------------------+
      t$ K& U: _) B( n/ i. Z| codesize() -> size:u256                     | 当前合约/执行上下文的代码大小                                  |
    ! i4 i$ c- n. {4 Z( }% I±--------------------------------------------±----------------------------------------------------------------+
    . R; i4 s& `+ B* q6 E* X0 e/ w& n| codecopy(t:u256, f:u256, s:u256)            | 从 code 位置 f 拷贝 s 字节到内存位置 t                          |
    , P+ `' J6 n* P' ~" H7 n# A±--------------------------------------------±----------------------------------------------------------------+5 |( v4 \* \7 _- \0 X' g4 K. Q
    | extcodesize(a:u256) -> size:u256            | 地址 a 上的代码大小                                             |
    5 c& s% S: T" `" W±--------------------------------------------±----------------------------------------------------------------+7 |- Z# v- H3 X  v9 e
    | extcodecopy(a:u256, t:u256, f:u256, s:u256) | 相当于 codecopy(t, f, s),但从地址 a 获取代码                   |& D$ ?1 v6 ~0 }; n- t
    ±--------------------------------------------±----------------------------------------------------------------+
    ' i9 E6 V% c1 L| 其他                                                                                                        |! r- B* |( @7 [# I3 I. |1 s' i
    ±--------------------------------------------±----------------------------------------------------------------+; m* }: `3 [6 _7 C' p# x, V
    | discard(unused:bool)                        | 丢弃值                                                          |& O$ G0 G. a- C$ \
    ±--------------------------------------------±----------------------------------------------------------------+3 n# Y1 Q& M8 X9 ^: f9 Y* K
    | discardu256(unused:u256)                    | 丢弃值                                                          |
    2 e5 L! O, X2 b, t5 z9 F±--------------------------------------------±----------------------------------------------------------------+
    ; x+ q; x0 J9 W5 }| splitu256tou64(x:u256) -> (x1:u64, x2:u64,  | 将一个 u256 拆分为四个 u64                                      |; g! C2 {- Y/ X4 y0 y& G/ ^) _! |
    |                            x3:u64, x4:u64)  |                                                                 |) T  C! ^3 v9 h9 |. A( {
    ±--------------------------------------------±----------------------------------------------------------------+, @) T& j( J# q3 A9 T" _% I" _
    | combineu64tou256(x1:u64, x2:u64, x3:u64,    | 将四个 u64 组合为一个 u256                                      |+ `! S( j" `+ o! v
    |                  x4:u64) -> (x:u256)        |                                                                 |
    ( `* w! [5 c+ k4 f6 _  ?±--------------------------------------------±----------------------------------------------------------------+$ |7 f! Z/ a1 ?' Y  k$ q; D
    | keccak256(p:u256, s:u256) -> v:u256         | keccak(mem[p…(p+s)))                                          |
    : r7 k$ ~* @4 E5 [5 x# s2 [±--------------------------------------------±----------------------------------------------------------------+1 A0 z# L; g5 z) ~  ~, {
    后端
    5 b' N- d2 V* }" d$ n后端或目标负责将 Yul 翻译到特定字节码。 每个后端都可以暴露以后端名称为前缀的函数。 我们为两个建议的后端保留 evm_ 和 ewasm_ 前缀。
    ( n1 ~' x- N1 E  R5 k+ s后端: EVM
    * B; d0 F: V, m9 Z2 K) Z% E7 w目标 |evm| 将具有所有用 evm_ 前缀暴露的 |evm| 底层操作码。
    # q( n& D, c( ?$ F后端: “EVM 1.5”
    8 J) l% C7 Z1 h' y) ?0 \TBD2 U4 J- F( V' g6 p. V, R
    后端: eWASM4 \; A3 ?+ k! J' [+ ?
    TBD
    * d! j2 u7 ~& ~: F, j8 a% a& yYul 对象说明" m3 S' p: [/ @/ u$ }
    语法::* s4 A! a0 \4 B
    顶层对象 = 'object' '{' 代码? ( 对象 | 数据 )* '}'3 J" F: d4 x2 t9 b# `  c
    对象 = 'object' 字符串字面量 '{' 代码? ( 对象 | 数据 )* '}'. J/ B+ v7 @) o, K2 W
    代码 = 'code' 代码块' B8 E- g- m9 h# x4 G7 L9 D. Z
    数据 = 'data' 字符串字面量 十六进制字面量
    2 }2 y8 b- M. K1 E) r- f9 f7 ~3 [十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
    3 a$ f  \1 i. i/ h) N& h2 t+ w- f7 M字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'
    5 A7 m- L4 ]6 c0 S! A在上面,代码块 指的是前一章中解释的 Yul 代码语法中的 代码块。
    . K5 z0 H  m% S' f9 J3 N. dYul 对象示例如下:
    + N* x: O( |/ [' O1 F: _…code::. s/ w* _4 w4 i. K4 J, f
    // 代码由单个对象组成。 单个 “code” 节点是对象的代码。
    4 N' G* U* V/ {6 l, U; A8 `// 每个(其他)命名的对象或数据部分都被序列化* x% g9 n9 n, p4 i# |& Y3 p4 u
    // 并可供特殊内置函数:datacopy / dataoffset / datasize 用于访问$ @- s* f6 l6 _, U
    object {+ y6 s- r; Y/ B+ @) h8 v! {0 }
        code {
    ' y2 u/ p  k' y2 g* ]( M        let size = datasize("runtime")
    2 }1 ]: V; D" f) V7 \: ~2 b        let offset = allocate(size)4 s- Q# Q! C  v$ e6 A3 G' E
            // 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy
    ; @. M7 N$ i# i; g" t2 k* x        datacopy(dataoffset("runtime"), offset, size)
    ' ?0 n4 M) U  h( A6 L        // 这是一个构造函数,并且运行时代码会被返回
    5 a$ J" L1 V: A" R' |        return(offset, size)
    / R& ~" V8 a- u0 K& [  O    }
    * y* f2 `2 X6 n    data "Table2" hex"4123"6 A% k- `& t8 Q
        object "runtime" {
    " l1 x" v; N4 ^( {. W$ h        code {0 u7 A* O, t$ e" ^8 q* t+ }0 V
                // 运行时代码2 j; f5 O7 j+ ^) L1 D/ `, @
                let size = datasize("Contract2")
    - C- u1 c, T' h0 f. L            let offset = allocate(size)# B& K* H' k9 N2 m$ b1 C
                // 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy
    ( p- e5 x! a2 a6 f            datacopy(dataoffset("Contract2"), offset, size). W8 n+ u8 W) u& i8 p# w. w
                // 构造函数参数是一个数字 0x12344 S8 k7 [. J# m" o
                mstore(add(offset, size), 0x1234)
    ! D% [0 e: p) U$ Z6 @  I            create(offset, add(size, 32))
    8 h& @5 Q8 V" L) P& `2 A, U% Q        }
      ?) Y" \; ?, [' C- _        // 内嵌对象。使用场景是,外层是一个工厂合约,而 Contract2 将是由工厂生成的代码, {  {1 _! J/ ^/ |  a, A
            object "Contract2" {
    9 j+ z' _! r' o( @* k( c            code {6 i; Z# ]$ p) K
                    // 代码在这 ...7 K) a! ~) L/ y) M5 K
                }/ Z  {# B" X& S; C* P
                object "runtime" {' o1 r& L1 ]" \/ i0 ]# R
                    code {
    . {' @# |% s: |& w# V/ {8 A                    // 代码在这 ...2 \, a+ S0 c3 U$ T7 F& T8 e. b
                    }& u+ W& [, f6 G! k, J& ^4 s, i4 I, U6 K
                 }; Y: I# \4 y% B9 W) S
                 data "Table1" hex"4123"9 m' p0 B% a9 N5 K" {+ b
            }& K9 I2 \0 }  }# u9 w1 F
        }
    3 W- F/ S/ i1 H7 [$ A}
  • BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
    声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    成为第一个吐槽的人