github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/core/asm/compiler.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:33</date>
    10  //</624342614291779584>
    11  
    12  
    13  package asm
    14  
    15  import (
    16  	"fmt"
    17  	"math/big"
    18  	"os"
    19  	"strings"
    20  
    21  	"github.com/ethereum/go-ethereum/common/math"
    22  	"github.com/ethereum/go-ethereum/core/vm"
    23  )
    24  
    25  //编译器包含有关已分析源的信息
    26  //并持有程序的令牌。
    27  type Compiler struct {
    28  	tokens []token
    29  	binary []interface{}
    30  
    31  	labels map[string]int
    32  
    33  	pc, pos int
    34  
    35  	debug bool
    36  }
    37  
    38  //NewCompiler返回新分配的编译器。
    39  func NewCompiler(debug bool) *Compiler {
    40  	return &Compiler{
    41  		labels: make(map[string]int),
    42  		debug:  debug,
    43  	}
    44  }
    45  
    46  //feed将令牌馈送到ch,并由
    47  //编译器。
    48  //
    49  //feed是编译阶段的第一个步骤,因为它
    50  //收集程序中使用的标签并保留
    51  //用于确定位置的程序计数器
    52  //跳跃的目的地。标签不能用于
    53  //第二阶段推标签并确定正确的
    54  //位置。
    55  func (c *Compiler) Feed(ch <-chan token) {
    56  	for i := range ch {
    57  		switch i.typ {
    58  		case number:
    59  			num := math.MustParseBig256(i.text).Bytes()
    60  			if len(num) == 0 {
    61  				num = []byte{0}
    62  			}
    63  			c.pc += len(num)
    64  		case stringValue:
    65  			c.pc += len(i.text) - 2
    66  		case element:
    67  			c.pc++
    68  		case labelDef:
    69  			c.labels[i.text] = c.pc
    70  			c.pc++
    71  		case label:
    72  			c.pc += 5
    73  		}
    74  
    75  		c.tokens = append(c.tokens, i)
    76  	}
    77  	if c.debug {
    78  		fmt.Fprintln(os.Stderr, "found", len(c.labels), "labels")
    79  	}
    80  }
    81  
    82  //编译编译当前令牌并返回一个
    83  //可由EVM解释的二进制字符串
    84  //如果失败了就会出错。
    85  //
    86  //编译是编译阶段的第二个阶段
    87  //
    88  func (c *Compiler) Compile() (string, []error) {
    89  	var errors []error
    90  //继续循环令牌,直到
    91  //
    92  	for c.pos < len(c.tokens) {
    93  		if err := c.compileLine(); err != nil {
    94  			errors = append(errors, err)
    95  		}
    96  	}
    97  
    98  //将二进制转换为十六进制
    99  	var bin string
   100  	for _, v := range c.binary {
   101  		switch v := v.(type) {
   102  		case vm.OpCode:
   103  			bin += fmt.Sprintf("%x", []byte{byte(v)})
   104  		case []byte:
   105  			bin += fmt.Sprintf("%x", v)
   106  		}
   107  	}
   108  	return bin, errors
   109  }
   110  
   111  //next返回下一个标记并递增
   112  //位置。
   113  func (c *Compiler) next() token {
   114  	token := c.tokens[c.pos]
   115  	c.pos++
   116  	return token
   117  }
   118  
   119  //compileline编译单行指令,例如
   120  //“push 1”,“jump@label”。
   121  func (c *Compiler) compileLine() error {
   122  	n := c.next()
   123  	if n.typ != lineStart {
   124  		return compileErr(n, n.typ.String(), lineStart.String())
   125  	}
   126  
   127  	lvalue := c.next()
   128  	switch lvalue.typ {
   129  	case eof:
   130  		return nil
   131  	case element:
   132  		if err := c.compileElement(lvalue); err != nil {
   133  			return err
   134  		}
   135  	case labelDef:
   136  		c.compileLabel()
   137  	case lineEnd:
   138  		return nil
   139  	default:
   140  		return compileErr(lvalue, lvalue.text, fmt.Sprintf("%v or %v", labelDef, element))
   141  	}
   142  
   143  	if n := c.next(); n.typ != lineEnd {
   144  		return compileErr(n, n.text, lineEnd.String())
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  //compileNumber将数字编译为字节
   151  func (c *Compiler) compileNumber(element token) (int, error) {
   152  	num := math.MustParseBig256(element.text).Bytes()
   153  	if len(num) == 0 {
   154  		num = []byte{0}
   155  	}
   156  	c.pushBin(num)
   157  	return len(num), nil
   158  }
   159  
   160  //compileElement编译元素(push&label或两者兼有)
   161  //以二进制表示,如果语句不正确,则可能出错。
   162  //喂的地方。
   163  func (c *Compiler) compileElement(element token) error {
   164  //检查是否有跳跃。必须读取和编译跳转
   165  //从右到左。
   166  	if isJump(element.text) {
   167  		rvalue := c.next()
   168  		switch rvalue.typ {
   169  		case number:
   170  //TODO了解如何正确返回错误
   171  			c.compileNumber(rvalue)
   172  		case stringValue:
   173  //字符串被引用,请删除它们。
   174  			c.pushBin(rvalue.text[1 : len(rvalue.text)-2])
   175  		case label:
   176  			c.pushBin(vm.PUSH4)
   177  			pos := big.NewInt(int64(c.labels[rvalue.text])).Bytes()
   178  			pos = append(make([]byte, 4-len(pos)), pos...)
   179  			c.pushBin(pos)
   180  		default:
   181  			return compileErr(rvalue, rvalue.text, "number, string or label")
   182  		}
   183  //推动操作
   184  		c.pushBin(toBinary(element.text))
   185  		return nil
   186  	} else if isPush(element.text) {
   187  //把手推。按从左到右读取。
   188  		var value []byte
   189  
   190  		rvalue := c.next()
   191  		switch rvalue.typ {
   192  		case number:
   193  			value = math.MustParseBig256(rvalue.text).Bytes()
   194  			if len(value) == 0 {
   195  				value = []byte{0}
   196  			}
   197  		case stringValue:
   198  			value = []byte(rvalue.text[1 : len(rvalue.text)-1])
   199  		case label:
   200  			value = make([]byte, 4)
   201  			copy(value, big.NewInt(int64(c.labels[rvalue.text])).Bytes())
   202  		default:
   203  			return compileErr(rvalue, rvalue.text, "number, string or label")
   204  		}
   205  
   206  		if len(value) > 32 {
   207  			return fmt.Errorf("%d type error: unsupported string or number with size > 32", rvalue.lineno)
   208  		}
   209  
   210  		c.pushBin(vm.OpCode(int(vm.PUSH1) - 1 + len(value)))
   211  		c.pushBin(value)
   212  	} else {
   213  		c.pushBin(toBinary(element.text))
   214  	}
   215  
   216  	return nil
   217  }
   218  
   219  //compileLabel将JumpDest推送到二进制切片。
   220  func (c *Compiler) compileLabel() {
   221  	c.pushBin(vm.JUMPDEST)
   222  }
   223  
   224  //推杆将值V推送到二进制堆栈。
   225  func (c *Compiler) pushBin(v interface{}) {
   226  	if c.debug {
   227  		fmt.Printf("%d: %v\n", len(c.binary), v)
   228  	}
   229  	c.binary = append(c.binary, v)
   230  }
   231  
   232  //
   233  //推(n)。
   234  func isPush(op string) bool {
   235  	return strings.ToUpper(op) == "PUSH"
   236  }
   237  
   238  //is jump返回字符串op是否为jump(i)
   239  func isJump(op string) bool {
   240  	return strings.ToUpper(op) == "JUMPI" || strings.ToUpper(op) == "JUMP"
   241  }
   242  
   243  //ToBinary将文本转换为vm.opcode
   244  func toBinary(text string) vm.OpCode {
   245  	return vm.StringToOp(strings.ToUpper(text))
   246  }
   247  
   248  type compileError struct {
   249  	got  string
   250  	want string
   251  
   252  	lineno int
   253  }
   254  
   255  func (err compileError) Error() string {
   256  	return fmt.Sprintf("%d syntax error: unexpected %v, expected %v", err.lineno, err.got, err.want)
   257  }
   258  
   259  func compileErr(c token, got, want string) error {
   260  	return compileError{
   261  		got:    got,
   262  		want:   want,
   263  		lineno: c.lineno,
   264  	}
   265  }
   266