github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/core/asm/compiler.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:33</date> 10 //</624342614291779584> 11 12 13 package asm 14 15 import ( 16 "fmt" 17 "math/big" 18 "os" 19 "strings" 20 21 "github.com/ethereum/go-ethereum/common/math" 22 "github.com/ethereum/go-ethereum/core/vm" 23 ) 24 25 //编译器包含有关已分析源的信息 26 //并持有程序的令牌。 27 type Compiler struct { 28 tokens []token 29 binary []interface{} 30 31 labels map[string]int 32 33 pc, pos int 34 35 debug bool 36 } 37 38 //NewCompiler返回新分配的编译器。 39 func NewCompiler(debug bool) *Compiler { 40 return &Compiler{ 41 labels: make(map[string]int), 42 debug: debug, 43 } 44 } 45 46 //feed将令牌馈送到ch,并由 47 //编译器。 48 // 49 //feed是编译阶段的第一个步骤,因为它 50 //收集程序中使用的标签并保留 51 //用于确定位置的程序计数器 52 //跳跃的目的地。标签不能用于 53 //第二阶段推标签并确定正确的 54 //位置。 55 func (c *Compiler) Feed(ch <-chan token) { 56 for i := range ch { 57 switch i.typ { 58 case number: 59 num := math.MustParseBig256(i.text).Bytes() 60 if len(num) == 0 { 61 num = []byte{0} 62 } 63 c.pc += len(num) 64 case stringValue: 65 c.pc += len(i.text) - 2 66 case element: 67 c.pc++ 68 case labelDef: 69 c.labels[i.text] = c.pc 70 c.pc++ 71 case label: 72 c.pc += 5 73 } 74 75 c.tokens = append(c.tokens, i) 76 } 77 if c.debug { 78 fmt.Fprintln(os.Stderr, "found", len(c.labels), "labels") 79 } 80 } 81 82 //编译编译当前令牌并返回一个 83 //可由EVM解释的二进制字符串 84 //如果失败了就会出错。 85 // 86 //编译是编译阶段的第二个阶段 87 // 88 func (c *Compiler) Compile() (string, []error) { 89 var errors []error 90 //继续循环令牌,直到 91 // 92 for c.pos < len(c.tokens) { 93 if err := c.compileLine(); err != nil { 94 errors = append(errors, err) 95 } 96 } 97 98 //将二进制转换为十六进制 99 var bin string 100 for _, v := range c.binary { 101 switch v := v.(type) { 102 case vm.OpCode: 103 bin += fmt.Sprintf("%x", []byte{byte(v)}) 104 case []byte: 105 bin += fmt.Sprintf("%x", v) 106 } 107 } 108 return bin, errors 109 } 110 111 //next返回下一个标记并递增 112 //位置。 113 func (c *Compiler) next() token { 114 token := c.tokens[c.pos] 115 c.pos++ 116 return token 117 } 118 119 //compileline编译单行指令,例如 120 //“push 1”,“jump@label”。 121 func (c *Compiler) compileLine() error { 122 n := c.next() 123 if n.typ != lineStart { 124 return compileErr(n, n.typ.String(), lineStart.String()) 125 } 126 127 lvalue := c.next() 128 switch lvalue.typ { 129 case eof: 130 return nil 131 case element: 132 if err := c.compileElement(lvalue); err != nil { 133 return err 134 } 135 case labelDef: 136 c.compileLabel() 137 case lineEnd: 138 return nil 139 default: 140 return compileErr(lvalue, lvalue.text, fmt.Sprintf("%v or %v", labelDef, element)) 141 } 142 143 if n := c.next(); n.typ != lineEnd { 144 return compileErr(n, n.text, lineEnd.String()) 145 } 146 147 return nil 148 } 149 150 //compileNumber将数字编译为字节 151 func (c *Compiler) compileNumber(element token) (int, error) { 152 num := math.MustParseBig256(element.text).Bytes() 153 if len(num) == 0 { 154 num = []byte{0} 155 } 156 c.pushBin(num) 157 return len(num), nil 158 } 159 160 //compileElement编译元素(push&label或两者兼有) 161 //以二进制表示,如果语句不正确,则可能出错。 162 //喂的地方。 163 func (c *Compiler) compileElement(element token) error { 164 //检查是否有跳跃。必须读取和编译跳转 165 //从右到左。 166 if isJump(element.text) { 167 rvalue := c.next() 168 switch rvalue.typ { 169 case number: 170 //TODO了解如何正确返回错误 171 c.compileNumber(rvalue) 172 case stringValue: 173 //字符串被引用,请删除它们。 174 c.pushBin(rvalue.text[1 : len(rvalue.text)-2]) 175 case label: 176 c.pushBin(vm.PUSH4) 177 pos := big.NewInt(int64(c.labels[rvalue.text])).Bytes() 178 pos = append(make([]byte, 4-len(pos)), pos...) 179 c.pushBin(pos) 180 default: 181 return compileErr(rvalue, rvalue.text, "number, string or label") 182 } 183 //推动操作 184 c.pushBin(toBinary(element.text)) 185 return nil 186 } else if isPush(element.text) { 187 //把手推。按从左到右读取。 188 var value []byte 189 190 rvalue := c.next() 191 switch rvalue.typ { 192 case number: 193 value = math.MustParseBig256(rvalue.text).Bytes() 194 if len(value) == 0 { 195 value = []byte{0} 196 } 197 case stringValue: 198 value = []byte(rvalue.text[1 : len(rvalue.text)-1]) 199 case label: 200 value = make([]byte, 4) 201 copy(value, big.NewInt(int64(c.labels[rvalue.text])).Bytes()) 202 default: 203 return compileErr(rvalue, rvalue.text, "number, string or label") 204 } 205 206 if len(value) > 32 { 207 return fmt.Errorf("%d type error: unsupported string or number with size > 32", rvalue.lineno) 208 } 209 210 c.pushBin(vm.OpCode(int(vm.PUSH1) - 1 + len(value))) 211 c.pushBin(value) 212 } else { 213 c.pushBin(toBinary(element.text)) 214 } 215 216 return nil 217 } 218 219 //compileLabel将JumpDest推送到二进制切片。 220 func (c *Compiler) compileLabel() { 221 c.pushBin(vm.JUMPDEST) 222 } 223 224 //推杆将值V推送到二进制堆栈。 225 func (c *Compiler) pushBin(v interface{}) { 226 if c.debug { 227 fmt.Printf("%d: %v\n", len(c.binary), v) 228 } 229 c.binary = append(c.binary, v) 230 } 231 232 // 233 //推(n)。 234 func isPush(op string) bool { 235 return strings.ToUpper(op) == "PUSH" 236 } 237 238 //is jump返回字符串op是否为jump(i) 239 func isJump(op string) bool { 240 return strings.ToUpper(op) == "JUMPI" || strings.ToUpper(op) == "JUMP" 241 } 242 243 //ToBinary将文本转换为vm.opcode 244 func toBinary(text string) vm.OpCode { 245 return vm.StringToOp(strings.ToUpper(text)) 246 } 247 248 type compileError struct { 249 got string 250 want string 251 252 lineno int 253 } 254 255 func (err compileError) Error() string { 256 return fmt.Sprintf("%d syntax error: unexpected %v, expected %v", err.lineno, err.got, err.want) 257 } 258 259 func compileErr(c token, got, want string) error { 260 return compileError{ 261 got: got, 262 want: want, 263 lineno: c.lineno, 264 } 265 } 266