compiler.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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. package asm
  17. import (
  18. "fmt"
  19. "math/big"
  20. "os"
  21. "strings"
  22. "github.com/ethereum/go-ethereum/common/math"
  23. "github.com/ethereum/go-ethereum/core/vm"
  24. )
  25. // Compiler contains information about the parsed source
  26. // and holds the tokens for the program.
  27. type Compiler struct {
  28. tokens []token
  29. binary []interface{}
  30. labels map[string]int
  31. pc, pos int
  32. debug bool
  33. }
  34. // newCompiler returns a new allocated compiler.
  35. func NewCompiler(debug bool) *Compiler {
  36. return &Compiler{
  37. labels: make(map[string]int),
  38. debug: debug,
  39. }
  40. }
  41. // Feed feeds tokens in to ch and are interpreted by
  42. // the compiler.
  43. //
  44. // feed is the first pass in the compile stage as it
  45. // collects the used labels in the program and keeps a
  46. // program counter which is used to determine the locations
  47. // of the jump dests. The labels can than be used in the
  48. // second stage to push labels and determine the right
  49. // position.
  50. func (c *Compiler) Feed(ch <-chan token) {
  51. var prev token
  52. for i := range ch {
  53. switch i.typ {
  54. case number:
  55. num := math.MustParseBig256(i.text).Bytes()
  56. if len(num) == 0 {
  57. num = []byte{0}
  58. }
  59. c.pc += len(num)
  60. case stringValue:
  61. c.pc += len(i.text) - 2
  62. case element:
  63. c.pc++
  64. case labelDef:
  65. c.labels[i.text] = c.pc
  66. c.pc++
  67. case label:
  68. c.pc += 4
  69. if prev.typ == element && isJump(prev.text) {
  70. c.pc++
  71. }
  72. }
  73. c.tokens = append(c.tokens, i)
  74. prev = i
  75. }
  76. if c.debug {
  77. fmt.Fprintln(os.Stderr, "found", len(c.labels), "labels")
  78. }
  79. }
  80. // Compile compiles the current tokens and returns a
  81. // binary string that can be interpreted by the EVM
  82. // and an error if it failed.
  83. //
  84. // compile is the second stage in the compile phase
  85. // which compiles the tokens to EVM instructions.
  86. func (c *Compiler) Compile() (string, []error) {
  87. var errors []error
  88. // continue looping over the tokens until
  89. // the stack has been exhausted.
  90. for c.pos < len(c.tokens) {
  91. if err := c.compileLine(); err != nil {
  92. errors = append(errors, err)
  93. }
  94. }
  95. // turn the binary to hex
  96. var bin string
  97. for _, v := range c.binary {
  98. switch v := v.(type) {
  99. case vm.OpCode:
  100. bin += fmt.Sprintf("%x", []byte{byte(v)})
  101. case []byte:
  102. bin += fmt.Sprintf("%x", v)
  103. }
  104. }
  105. return bin, errors
  106. }
  107. // next returns the next token and increments the
  108. // position.
  109. func (c *Compiler) next() token {
  110. token := c.tokens[c.pos]
  111. c.pos++
  112. return token
  113. }
  114. // compileLine compiles a single line instruction e.g.
  115. // "push 1", "jump @label".
  116. func (c *Compiler) compileLine() error {
  117. n := c.next()
  118. if n.typ != lineStart {
  119. return compileErr(n, n.typ.String(), lineStart.String())
  120. }
  121. lvalue := c.next()
  122. switch lvalue.typ {
  123. case eof:
  124. return nil
  125. case element:
  126. if err := c.compileElement(lvalue); err != nil {
  127. return err
  128. }
  129. case labelDef:
  130. c.compileLabel()
  131. case lineEnd:
  132. return nil
  133. default:
  134. return compileErr(lvalue, lvalue.text, fmt.Sprintf("%v or %v", labelDef, element))
  135. }
  136. if n := c.next(); n.typ != lineEnd {
  137. return compileErr(n, n.text, lineEnd.String())
  138. }
  139. return nil
  140. }
  141. // compileNumber compiles the number to bytes
  142. func (c *Compiler) compileNumber(element token) (int, error) {
  143. num := math.MustParseBig256(element.text).Bytes()
  144. if len(num) == 0 {
  145. num = []byte{0}
  146. }
  147. c.pushBin(num)
  148. return len(num), nil
  149. }
  150. // compileElement compiles the element (push & label or both)
  151. // to a binary representation and may error if incorrect statements
  152. // where fed.
  153. func (c *Compiler) compileElement(element token) error {
  154. // check for a jump. jumps must be read and compiled
  155. // from right to left.
  156. if isJump(element.text) {
  157. rvalue := c.next()
  158. switch rvalue.typ {
  159. case number:
  160. // TODO figure out how to return the error properly
  161. c.compileNumber(rvalue)
  162. case stringValue:
  163. // strings are quoted, remove them.
  164. c.pushBin(rvalue.text[1 : len(rvalue.text)-2])
  165. case label:
  166. c.pushBin(vm.PUSH4)
  167. pos := big.NewInt(int64(c.labels[rvalue.text])).Bytes()
  168. pos = append(make([]byte, 4-len(pos)), pos...)
  169. c.pushBin(pos)
  170. case lineEnd:
  171. c.pos--
  172. default:
  173. return compileErr(rvalue, rvalue.text, "number, string or label")
  174. }
  175. // push the operation
  176. c.pushBin(toBinary(element.text))
  177. return nil
  178. } else if isPush(element.text) {
  179. // handle pushes. pushes are read from left to right.
  180. var value []byte
  181. rvalue := c.next()
  182. switch rvalue.typ {
  183. case number:
  184. value = math.MustParseBig256(rvalue.text).Bytes()
  185. if len(value) == 0 {
  186. value = []byte{0}
  187. }
  188. case stringValue:
  189. value = []byte(rvalue.text[1 : len(rvalue.text)-1])
  190. case label:
  191. value = big.NewInt(int64(c.labels[rvalue.text])).Bytes()
  192. value = append(make([]byte, 4-len(value)), value...)
  193. default:
  194. return compileErr(rvalue, rvalue.text, "number, string or label")
  195. }
  196. if len(value) > 32 {
  197. return fmt.Errorf("%d type error: unsupported string or number with size > 32", rvalue.lineno)
  198. }
  199. c.pushBin(vm.OpCode(int(vm.PUSH1) - 1 + len(value)))
  200. c.pushBin(value)
  201. } else {
  202. c.pushBin(toBinary(element.text))
  203. }
  204. return nil
  205. }
  206. // compileLabel pushes a jumpdest to the binary slice.
  207. func (c *Compiler) compileLabel() {
  208. c.pushBin(vm.JUMPDEST)
  209. }
  210. // pushBin pushes the value v to the binary stack.
  211. func (c *Compiler) pushBin(v interface{}) {
  212. if c.debug {
  213. fmt.Printf("%d: %v\n", len(c.binary), v)
  214. }
  215. c.binary = append(c.binary, v)
  216. }
  217. // isPush returns whether the string op is either any of
  218. // push(N).
  219. func isPush(op string) bool {
  220. return strings.ToUpper(op) == "PUSH"
  221. }
  222. // isJump returns whether the string op is jump(i)
  223. func isJump(op string) bool {
  224. return strings.ToUpper(op) == "JUMPI" || strings.ToUpper(op) == "JUMP"
  225. }
  226. // toBinary converts text to a vm.OpCode
  227. func toBinary(text string) vm.OpCode {
  228. return vm.StringToOp(strings.ToUpper(text))
  229. }
  230. type compileError struct {
  231. got string
  232. want string
  233. lineno int
  234. }
  235. func (err compileError) Error() string {
  236. return fmt.Sprintf("%d syntax error: unexpected %v, expected %v", err.lineno, err.got, err.want)
  237. }
  238. func compileErr(c token, got, want string) error {
  239. return compileError{
  240. got: got,
  241. want: want,
  242. lineno: c.lineno,
  243. }
  244. }