github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/internal/jsre/jsre.go (about)

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