Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Yul 语言说明

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

    本版积分规则

    成为第一个吐槽的人