github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/internal/jsre/jsre.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:40</date>
    10  //</624342642527834112>
    11  
    12  
    13  //包JSRE为JavaScript提供执行环境。
    14  package jsre
    15  
    16  import (
    17  	crand "crypto/rand"
    18  	"encoding/binary"
    19  	"fmt"
    20  	"io"
    21  	"io/ioutil"
    22  	"math/rand"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/internal/jsre/deps"
    27  	"github.com/robertkrimen/otto"
    28  )
    29  
    30  var (
    31  	BigNumber_JS = deps.MustAsset("bignumber.js")
    32  	Web3_JS      = deps.MustAsset("web3.js")
    33  )
    34  
    35  /*
    36  JSRE是嵌入OttoJS解释器的通用JS运行时环境。
    37  它为
    38  -从文件加载代码
    39  -运行代码段
    40  -需要库
    41  -绑定本机go对象
    42  **/
    43  
    44  type JSRE struct {
    45  	assetPath     string
    46  	output        io.Writer
    47  	evalQueue     chan *evalReq
    48  	stopEventLoop chan bool
    49  	closed        chan struct{}
    50  }
    51  
    52  //JSTimer是带有回调函数的单个计时器实例
    53  type jsTimer struct {
    54  	timer    *time.Timer
    55  	duration time.Duration
    56  	interval bool
    57  	call     otto.FunctionCall
    58  }
    59  
    60  //evalReq是一个由runEventLoop处理的序列化VM执行请求。
    61  type evalReq struct {
    62  	fn   func(vm *otto.Otto)
    63  	done chan bool
    64  }
    65  
    66  //运行时在使用后必须使用stop()停止,在停止后不能使用。
    67  func New(assetPath string, output io.Writer) *JSRE {
    68  	re := &JSRE{
    69  		assetPath:     assetPath,
    70  		output:        output,
    71  		closed:        make(chan struct{}),
    72  		evalQueue:     make(chan *evalReq),
    73  		stopEventLoop: make(chan bool),
    74  	}
    75  	go re.runEventLoop()
    76  	re.Set("loadScript", re.loadScript)
    77  	re.Set("inspect", re.prettyPrintJS)
    78  	return re
    79  }
    80  
    81  //RandomSource返回一个伪随机值生成器。
    82  func randomSource() *rand.Rand {
    83  	bytes := make([]byte, 8)
    84  	seed := time.Now().UnixNano()
    85  	if _, err := crand.Read(bytes); err == nil {
    86  		seed = int64(binary.LittleEndian.Uint64(bytes))
    87  	}
    88  
    89  	src := rand.NewSource(seed)
    90  	return rand.New(src)
    91  }
    92  
    93  //此函数从启动的goroutine运行主事件循环
    94  //创建JSRE时。在退出之前使用stop()来正确地停止它。
    95  //事件循环处理来自evalqueue的VM访问请求
    96  //序列化方式,并在适当的时间调用计时器回调函数。
    97  
    98  //导出的函数总是通过事件队列访问VM。你可以
    99  //直接调用OttoVM的函数以绕过队列。这些
   100  //当且仅当运行的例程
   101  //通过RPC调用从JS调用。
   102  func (re *JSRE) runEventLoop() {
   103  	defer close(re.closed)
   104  
   105  	vm := otto.New()
   106  	r := randomSource()
   107  	vm.SetRandomSource(r.Float64)
   108  
   109  	registry := map[*jsTimer]*jsTimer{}
   110  	ready := make(chan *jsTimer)
   111  
   112  	newTimer := func(call otto.FunctionCall, interval bool) (*jsTimer, otto.Value) {
   113  		delay, _ := call.Argument(1).ToInteger()
   114  		if 0 >= delay {
   115  			delay = 1
   116  		}
   117  		timer := &jsTimer{
   118  			duration: time.Duration(delay) * time.Millisecond,
   119  			call:     call,
   120  			interval: interval,
   121  		}
   122  		registry[timer] = timer
   123  
   124  		timer.timer = time.AfterFunc(timer.duration, func() {
   125  			ready <- timer
   126  		})
   127  
   128  		value, err := call.Otto.ToValue(timer)
   129  		if err != nil {
   130  			panic(err)
   131  		}
   132  		return timer, value
   133  	}
   134  
   135  	setTimeout := func(call otto.FunctionCall) otto.Value {
   136  		_, value := newTimer(call, false)
   137  		return value
   138  	}
   139  
   140  	setInterval := func(call otto.FunctionCall) otto.Value {
   141  		_, value := newTimer(call, true)
   142  		return value
   143  	}
   144  
   145  	clearTimeout := func(call otto.FunctionCall) otto.Value {
   146  		timer, _ := call.Argument(0).Export()
   147  		if timer, ok := timer.(*jsTimer); ok {
   148  			timer.timer.Stop()
   149  			delete(registry, timer)
   150  		}
   151  		return otto.UndefinedValue()
   152  	}
   153  	vm.Set("_setTimeout", setTimeout)
   154  	vm.Set("_setInterval", setInterval)
   155  	vm.Run(`var setTimeout = function(args) {
   156  		if (arguments.length < 1) {
   157  			throw TypeError("Failed to execute 'setTimeout': 1 argument required, but only 0 present.");
   158  		}
   159  		return _setTimeout.apply(this, arguments);
   160  	}`)
   161  	vm.Run(`var setInterval = function(args) {
   162  		if (arguments.length < 1) {
   163  			throw TypeError("Failed to execute 'setInterval': 1 argument required, but only 0 present.");
   164  		}
   165  		return _setInterval.apply(this, arguments);
   166  	}`)
   167  	vm.Set("clearTimeout", clearTimeout)
   168  	vm.Set("clearInterval", clearTimeout)
   169  
   170  	var waitForCallbacks bool
   171  
   172  loop:
   173  	for {
   174  		select {
   175  		case timer := <-ready:
   176  //执行回调,删除/重新安排计时器
   177  			var arguments []interface{}
   178  			if len(timer.call.ArgumentList) > 2 {
   179  				tmp := timer.call.ArgumentList[2:]
   180  				arguments = make([]interface{}, 2+len(tmp))
   181  				for i, value := range tmp {
   182  					arguments[i+2] = value
   183  				}
   184  			} else {
   185  				arguments = make([]interface{}, 1)
   186  			}
   187  			arguments[0] = timer.call.ArgumentList[0]
   188  			_, err := vm.Call(`Function.call.call`, nil, arguments...)
   189  			if err != nil {
   190  				fmt.Println("js error:", err, arguments)
   191  			}
   192  
   193  _, inreg := registry[timer] //当从回调中调用ClearInterval时,不要重置它
   194  			if timer.interval && inreg {
   195  				timer.timer.Reset(timer.duration)
   196  			} else {
   197  				delete(registry, timer)
   198  				if waitForCallbacks && (len(registry) == 0) {
   199  					break loop
   200  				}
   201  			}
   202  		case req := <-re.evalQueue:
   203  //运行代码,返回结果
   204  			req.fn(vm)
   205  			close(req.done)
   206  			if waitForCallbacks && (len(registry) == 0) {
   207  				break loop
   208  			}
   209  		case waitForCallbacks = <-re.stopEventLoop:
   210  			if !waitForCallbacks || (len(registry) == 0) {
   211  				break loop
   212  			}
   213  		}
   214  	}
   215  
   216  	for _, timer := range registry {
   217  		timer.timer.Stop()
   218  		delete(registry, timer)
   219  	}
   220  }
   221  
   222  //do在JS事件循环上执行给定的函数。
   223  func (re *JSRE) Do(fn func(*otto.Otto)) {
   224  	done := make(chan bool)
   225  	req := &evalReq{fn, done}
   226  	re.evalQueue <- req
   227  	<-done
   228  }
   229  
   230  //在退出前停止事件循环,或者等待所有计时器过期
   231  func (re *JSRE) Stop(waitForCallbacks bool) {
   232  	select {
   233  	case <-re.closed:
   234  	case re.stopEventLoop <- waitForCallbacks:
   235  		<-re.closed
   236  	}
   237  }
   238  
   239  //exec(文件)加载并运行文件的内容
   240  //如果给定了相对路径,则使用JSRE的assetpath
   241  func (re *JSRE) Exec(file string) error {
   242  	code, err := ioutil.ReadFile(common.AbsolutePath(re.assetPath, file))
   243  	if err != nil {
   244  		return err
   245  	}
   246  	var script *otto.Script
   247  	re.Do(func(vm *otto.Otto) {
   248  		script, err = vm.Compile(file, code)
   249  		if err != nil {
   250  			return
   251  		}
   252  		_, err = vm.Run(script)
   253  	})
   254  	return err
   255  }
   256  
   257  //bind将值v赋给JS环境中的变量
   258  //此方法已弃用,请使用set。
   259  func (re *JSRE) Bind(name string, v interface{}) error {
   260  	return re.Set(name, v)
   261  }
   262  
   263  //运行运行一段JS代码。
   264  func (re *JSRE) Run(code string) (v otto.Value, err error) {
   265  	re.Do(func(vm *otto.Otto) { v, err = vm.Run(code) })
   266  	return v, err
   267  }
   268  
   269  //get返回JS环境中变量的值。
   270  func (re *JSRE) Get(ns string) (v otto.Value, err error) {
   271  	re.Do(func(vm *otto.Otto) { v, err = vm.Get(ns) })
   272  	return v, err
   273  }
   274  
   275  //set将值v赋给JS环境中的变量。
   276  func (re *JSRE) Set(ns string, v interface{}) (err error) {
   277  	re.Do(func(vm *otto.Otto) { err = vm.Set(ns, v) })
   278  	return err
   279  }
   280  
   281  //loadscript从当前执行的JS代码内部执行JS脚本。
   282  func (re *JSRE) loadScript(call otto.FunctionCall) otto.Value {
   283  	file, err := call.Argument(0).ToString()
   284  	if err != nil {
   285  //TODO:引发异常
   286  		return otto.FalseValue()
   287  	}
   288  	file = common.AbsolutePath(re.assetPath, file)
   289  	source, err := ioutil.ReadFile(file)
   290  	if err != nil {
   291  //TODO:引发异常
   292  		return otto.FalseValue()
   293  	}
   294  	if _, err := compileAndRun(call.Otto, file, source); err != nil {
   295  //TODO:引发异常
   296  		fmt.Println("err:", err)
   297  		return otto.FalseValue()
   298  	}
   299  //TODO:返回评估结果
   300  	return otto.TrueValue()
   301  }
   302  
   303  //evaluate执行代码并将结果漂亮地打印到指定的输出
   304  //溪流。
   305  func (re *JSRE) Evaluate(code string, w io.Writer) error {
   306  	var fail error
   307  
   308  	re.Do(func(vm *otto.Otto) {
   309  		val, err := vm.Run(code)
   310  		if err != nil {
   311  			prettyError(vm, err, w)
   312  		} else {
   313  			prettyPrint(vm, val, w)
   314  		}
   315  		fmt.Fprintln(w)
   316  	})
   317  	return fail
   318  }
   319  
   320  //编译然后运行一段JS代码。
   321  func (re *JSRE) Compile(filename string, src interface{}) (err error) {
   322  	re.Do(func(vm *otto.Otto) { _, err = compileAndRun(vm, filename, src) })
   323  	return err
   324  }
   325  
   326  func compileAndRun(vm *otto.Otto, filename string, src interface{}) (otto.Value, error) {
   327  	script, err := vm.Compile(filename, src)
   328  	if err != nil {
   329  		return otto.Value{}, err
   330  	}
   331  	return vm.Run(script)
   332  }
   333