github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/core/asm/compiler.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package asm 18 19 import ( 20 "encoding/hex" 21 "errors" 22 "fmt" 23 "math/big" 24 "os" 25 "strings" 26 27 "github.com/ethereum/go-ethereum/common/math" 28 "github.com/ethereum/go-ethereum/core/vm" 29 ) 30 31 // Compiler contains information about the parsed source 32 // and holds the tokens for the program. 33 type Compiler struct { 34 tokens []token 35 out []byte 36 37 labels map[string]int 38 39 pc, pos int 40 41 debug bool 42 } 43 44 // NewCompiler returns a new allocated compiler. 45 func NewCompiler(debug bool) *Compiler { 46 return &Compiler{ 47 labels: make(map[string]int), 48 debug: debug, 49 } 50 } 51 52 // Feed feeds tokens into ch and are interpreted by 53 // the compiler. 54 // 55 // feed is the first pass in the compile stage as it collects the used labels in the 56 // program and keeps a program counter which is used to determine the locations of the 57 // jump dests. The labels can than be used in the second stage to push labels and 58 // determine the right position. 59 func (c *Compiler) Feed(ch <-chan token) { 60 var prev token 61 for i := range ch { 62 switch i.typ { 63 case number: 64 num := math.MustParseBig256(i.text).Bytes() 65 if len(num) == 0 { 66 num = []byte{0} 67 } 68 c.pc += len(num) 69 case stringValue: 70 c.pc += len(i.text) - 2 71 case element: 72 c.pc++ 73 case labelDef: 74 c.labels[i.text] = c.pc 75 c.pc++ 76 case label: 77 c.pc += 4 78 if prev.typ == element && isJump(prev.text) { 79 c.pc++ 80 } 81 } 82 c.tokens = append(c.tokens, i) 83 prev = i 84 } 85 if c.debug { 86 fmt.Fprintln(os.Stderr, "found", len(c.labels), "labels") 87 } 88 } 89 90 // Compile compiles the current tokens and returns a binary string that can be interpreted 91 // by the EVM and an error if it failed. 92 // 93 // compile is the second stage in the compile phase which compiles the tokens to EVM 94 // instructions. 95 func (c *Compiler) Compile() (string, []error) { 96 var errors []error 97 // continue looping over the tokens until 98 // the stack has been exhausted. 99 for c.pos < len(c.tokens) { 100 if err := c.compileLine(); err != nil { 101 errors = append(errors, err) 102 } 103 } 104 105 // turn the binary to hex 106 h := hex.EncodeToString(c.out) 107 return h, errors 108 } 109 110 // next returns the next token and increments the 111 // position. 112 func (c *Compiler) next() token { 113 token := c.tokens[c.pos] 114 c.pos++ 115 return token 116 } 117 118 // compileLine compiles a single line instruction e.g. 119 // "push 1", "jump @label". 120 func (c *Compiler) compileLine() error { 121 n := c.next() 122 if n.typ != lineStart { 123 return compileErr(n, n.typ.String(), lineStart.String()) 124 } 125 126 lvalue := c.next() 127 switch lvalue.typ { 128 case eof: 129 return nil 130 case element: 131 if err := c.compileElement(lvalue); err != nil { 132 return err 133 } 134 case labelDef: 135 c.compileLabel() 136 case lineEnd: 137 return nil 138 default: 139 return compileErr(lvalue, lvalue.text, fmt.Sprintf("%v or %v", labelDef, element)) 140 } 141 142 if n := c.next(); n.typ != lineEnd { 143 return compileErr(n, n.text, lineEnd.String()) 144 } 145 146 return nil 147 } 148 149 // parseNumber compiles the number to bytes 150 func parseNumber(tok token) ([]byte, error) { 151 if tok.typ != number { 152 panic("parseNumber of non-number token") 153 } 154 num, ok := math.ParseBig256(tok.text) 155 if !ok { 156 return nil, errors.New("invalid number") 157 } 158 bytes := num.Bytes() 159 if len(bytes) == 0 { 160 bytes = []byte{0} 161 } 162 return bytes, nil 163 } 164 165 // compileElement compiles the element (push & label or both) 166 // to a binary representation and may error if incorrect statements 167 // where fed. 168 func (c *Compiler) compileElement(element token) error { 169 switch { 170 case isJump(element.text): 171 return c.compileJump(element.text) 172 case isPush(element.text): 173 return c.compilePush() 174 default: 175 c.outputOpcode(toBinary(element.text)) 176 return nil 177 } 178 } 179 180 func (c *Compiler) compileJump(jumpType string) error { 181 rvalue := c.next() 182 switch rvalue.typ { 183 case number: 184 numBytes, err := parseNumber(rvalue) 185 if err != nil { 186 return err 187 } 188 c.outputBytes(numBytes) 189 190 case stringValue: 191 // strings are quoted, remove them. 192 str := rvalue.text[1 : len(rvalue.text)-2] 193 c.outputBytes([]byte(str)) 194 195 case label: 196 c.outputOpcode(vm.PUSH4) 197 pos := big.NewInt(int64(c.labels[rvalue.text])).Bytes() 198 pos = append(make([]byte, 4-len(pos)), pos...) 199 c.outputBytes(pos) 200 201 case lineEnd: 202 // push without argument is supported, it just takes the destination from the stack. 203 c.pos-- 204 205 default: 206 return compileErr(rvalue, rvalue.text, "number, string or label") 207 } 208 // push the operation 209 c.outputOpcode(toBinary(jumpType)) 210 return nil 211 } 212 213 func (c *Compiler) compilePush() error { 214 // handle pushes. pushes are read from left to right. 215 var value []byte 216 rvalue := c.next() 217 switch rvalue.typ { 218 case number: 219 value = math.MustParseBig256(rvalue.text).Bytes() 220 if len(value) == 0 { 221 value = []byte{0} 222 } 223 case stringValue: 224 value = []byte(rvalue.text[1 : len(rvalue.text)-1]) 225 case label: 226 value = big.NewInt(int64(c.labels[rvalue.text])).Bytes() 227 value = append(make([]byte, 4-len(value)), value...) 228 default: 229 return compileErr(rvalue, rvalue.text, "number, string or label") 230 } 231 if len(value) > 32 { 232 return fmt.Errorf("%d: string or number size > 32 bytes", rvalue.lineno+1) 233 } 234 c.outputOpcode(vm.OpCode(int(vm.PUSH1) - 1 + len(value))) 235 c.outputBytes(value) 236 return nil 237 } 238 239 // compileLabel pushes a jumpdest to the binary slice. 240 func (c *Compiler) compileLabel() { 241 c.outputOpcode(vm.JUMPDEST) 242 } 243 244 func (c *Compiler) outputOpcode(op vm.OpCode) { 245 if c.debug { 246 fmt.Printf("%d: %v\n", len(c.out), op) 247 } 248 c.out = append(c.out, byte(op)) 249 } 250 251 // output pushes the value v to the binary stack. 252 func (c *Compiler) outputBytes(b []byte) { 253 if c.debug { 254 fmt.Printf("%d: %x\n", len(c.out), b) 255 } 256 c.out = append(c.out, b...) 257 } 258 259 // isPush returns whether the string op is either any of 260 // push(N). 261 func isPush(op string) bool { 262 return strings.EqualFold(op, "PUSH") 263 } 264 265 // isJump returns whether the string op is jump(i) 266 func isJump(op string) bool { 267 return strings.EqualFold(op, "JUMPI") || strings.EqualFold(op, "JUMP") 268 } 269 270 // toBinary converts text to a vm.OpCode 271 func toBinary(text string) vm.OpCode { 272 return vm.StringToOp(strings.ToUpper(text)) 273 } 274 275 type compileError struct { 276 got string 277 want string 278 279 lineno int 280 } 281 282 func (err compileError) Error() string { 283 return fmt.Sprintf("%d: syntax error: unexpected %v, expected %v", err.lineno, err.got, err.want) 284 } 285 286 func compileErr(c token, got, want string) error { 287 return compileError{ 288 got: got, 289 want: want, 290 lineno: c.lineno + 1, 291 } 292 }