compiler.go 6.7 KB

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