compiler.go 6.5 KB

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