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