github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/internal/jsre/jsre.go (about)

     1  package jsre
     2  
     3  import (
     4  	crand "crypto/rand"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"math/rand"
    10  	"time"
    11  
    12  	"github.com/neatlab/neatio/internal/jsre/deps"
    13  	"github.com/neatlab/neatio/utilities/common"
    14  	"github.com/robertkrimen/otto"
    15  )
    16  
    17  var (
    18  	BigNumber_JS = deps.MustAsset("bignumber.js")
    19  	Web3_JS      = deps.MustAsset("web3.js")
    20  )
    21  
    22  type JSRE struct {
    23  	assetPath     string
    24  	output        io.Writer
    25  	evalQueue     chan *evalReq
    26  	stopEventLoop chan bool
    27  	closed        chan struct{}
    28  }
    29  
    30  type jsTimer struct {
    31  	timer    *time.Timer
    32  	duration time.Duration
    33  	interval bool
    34  	call     otto.FunctionCall
    35  }
    36  
    37  type evalReq struct {
    38  	fn   func(vm *otto.Otto)
    39  	done chan bool
    40  }
    41  
    42  func New(assetPath string, output io.Writer) *JSRE {
    43  	re := &JSRE{
    44  		assetPath:     assetPath,
    45  		output:        output,
    46  		closed:        make(chan struct{}),
    47  		evalQueue:     make(chan *evalReq),
    48  		stopEventLoop: make(chan bool),
    49  	}
    50  	go re.runEventLoop()
    51  	re.Set("loadScript", re.loadScript)
    52  	re.Set("inspect", re.prettyPrintJS)
    53  	return re
    54  }
    55  
    56  func randomSource() *rand.Rand {
    57  	bytes := make([]byte, 8)
    58  	seed := time.Now().UnixNano()
    59  	if _, err := crand.Read(bytes); err == nil {
    60  		seed = int64(binary.LittleEndian.Uint64(bytes))
    61  	}
    62  
    63  	src := rand.NewSource(seed)
    64  	return rand.New(src)
    65  }
    66  
    67  func (self *JSRE) runEventLoop() {
    68  	defer close(self.closed)
    69  
    70  	vm := otto.New()
    71  	r := randomSource()
    72  	vm.SetRandomSource(r.Float64)
    73  
    74  	registry := map[*jsTimer]*jsTimer{}
    75  	ready := make(chan *jsTimer)
    76  
    77  	newTimer := func(call otto.FunctionCall, interval bool) (*jsTimer, otto.Value) {
    78  		delay, _ := call.Argument(1).ToInteger()
    79  		if 0 >= delay {
    80  			delay = 1
    81  		}
    82  		timer := &jsTimer{
    83  			duration: time.Duration(delay) * time.Millisecond,
    84  			call:     call,
    85  			interval: interval,
    86  		}
    87  		registry[timer] = timer
    88  
    89  		timer.timer = time.AfterFunc(timer.duration, func() {
    90  			ready <- timer
    91  		})
    92  
    93  		value, err := call.Otto.ToValue(timer)
    94  		if err != nil {
    95  			panic(err)
    96  		}
    97  		return timer, value
    98  	}
    99  
   100  	setTimeout := func(call otto.FunctionCall) otto.Value {
   101  		_, value := newTimer(call, false)
   102  		return value
   103  	}
   104  
   105  	setInterval := func(call otto.FunctionCall) otto.Value {
   106  		_, value := newTimer(call, true)
   107  		return value
   108  	}
   109  
   110  	clearTimeout := func(call otto.FunctionCall) otto.Value {
   111  		timer, _ := call.Argument(0).Export()
   112  		if timer, ok := timer.(*jsTimer); ok {
   113  			timer.timer.Stop()
   114  			delete(registry, timer)
   115  		}
   116  		return otto.UndefinedValue()
   117  	}
   118  	vm.Set("_setTimeout", setTimeout)
   119  	vm.Set("_setInterval", setInterval)
   120  	vm.Run(`var setTimeout = function(args) {
   121  		if (arguments.length < 1) {
   122  			throw TypeError("Failed to execute 'setTimeout': 1 argument required, but only 0 present.");
   123  		}
   124  		return _setTimeout.apply(this, arguments);
   125  	}`)
   126  	vm.Run(`var setInterval = function(args) {
   127  		if (arguments.length < 1) {
   128  			throw TypeError("Failed to execute 'setInterval': 1 argument required, but only 0 present.");
   129  		}
   130  		return _setInterval.apply(this, arguments);
   131  	}`)
   132  	vm.Set("clearTimeout", clearTimeout)
   133  	vm.Set("clearInterval", clearTimeout)
   134  
   135  	var waitForCallbacks bool
   136  
   137  loop:
   138  	for {
   139  		select {
   140  		case timer := <-ready:
   141  
   142  			var arguments []interface{}
   143  			if len(timer.call.ArgumentList) > 2 {
   144  				tmp := timer.call.ArgumentList[2:]
   145  				arguments = make([]interface{}, 2+len(tmp))
   146  				for i, value := range tmp {
   147  					arguments[i+2] = value
   148  				}
   149  			} else {
   150  				arguments = make([]interface{}, 1)
   151  			}
   152  			arguments[0] = timer.call.ArgumentList[0]
   153  			_, err := vm.Call(`Function.call.call`, nil, arguments...)
   154  			if err != nil {
   155  				fmt.Println("js error:", err, arguments)
   156  			}
   157  
   158  			_, inreg := registry[timer]
   159  			if timer.interval && inreg {
   160  				timer.timer.Reset(timer.duration)
   161  			} else {
   162  				delete(registry, timer)
   163  				if waitForCallbacks && (len(registry) == 0) {
   164  					break loop
   165  				}
   166  			}
   167  		case req := <-self.evalQueue:
   168  
   169  			req.fn(vm)
   170  			close(req.done)
   171  			if waitForCallbacks && (len(registry) == 0) {
   172  				break loop
   173  			}
   174  		case waitForCallbacks = <-self.stopEventLoop:
   175  			if !waitForCallbacks || (len(registry) == 0) {
   176  				break loop
   177  			}
   178  		}
   179  	}
   180  
   181  	for _, timer := range registry {
   182  		timer.timer.Stop()
   183  		delete(registry, timer)
   184  	}
   185  }
   186  
   187  func (self *JSRE) Do(fn func(*otto.Otto)) {
   188  	done := make(chan bool)
   189  	req := &evalReq{fn, done}
   190  	self.evalQueue <- req
   191  	<-done
   192  }
   193  
   194  func (self *JSRE) Stop(waitForCallbacks bool) {
   195  	select {
   196  	case <-self.closed:
   197  	case self.stopEventLoop <- waitForCallbacks:
   198  		<-self.closed
   199  	}
   200  }
   201  
   202  func (self *JSRE) Exec(file string) error {
   203  	code, err := ioutil.ReadFile(common.AbsolutePath(self.assetPath, file))
   204  	if err != nil {
   205  		return err
   206  	}
   207  	var script *otto.Script
   208  	self.Do(func(vm *otto.Otto) {
   209  		script, err = vm.Compile(file, code)
   210  		if err != nil {
   211  			return
   212  		}
   213  		_, err = vm.Run(script)
   214  	})
   215  	return err
   216  }
   217  
   218  func (self *JSRE) Bind(name string, v interface{}) error {
   219  	return self.Set(name, v)
   220  }
   221  
   222  func (self *JSRE) Run(code string) (v otto.Value, err error) {
   223  	self.Do(func(vm *otto.Otto) { v, err = vm.Run(code) })
   224  	return v, err
   225  }
   226  
   227  func (self *JSRE) Get(ns string) (v otto.Value, err error) {
   228  	self.Do(func(vm *otto.Otto) { v, err = vm.Get(ns) })
   229  	return v, err
   230  }
   231  
   232  func (self *JSRE) Set(ns string, v interface{}) (err error) {
   233  	self.Do(func(vm *otto.Otto) { err = vm.Set(ns, v) })
   234  	return err
   235  }
   236  
   237  func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value {
   238  	file, err := call.Argument(0).ToString()
   239  	if err != nil {
   240  
   241  		return otto.FalseValue()
   242  	}
   243  	file = common.AbsolutePath(self.assetPath, file)
   244  	source, err := ioutil.ReadFile(file)
   245  	if err != nil {
   246  
   247  		return otto.FalseValue()
   248  	}
   249  	if _, err := compileAndRun(call.Otto, file, source); err != nil {
   250  
   251  		fmt.Println("err:", err)
   252  		return otto.FalseValue()
   253  	}
   254  
   255  	return otto.TrueValue()
   256  }
   257  
   258  func (self *JSRE) Evaluate(code string, w io.Writer) error {
   259  	var fail error
   260  
   261  	self.Do(func(vm *otto.Otto) {
   262  		val, err := vm.Run(code)
   263  		if err != nil {
   264  			prettyError(vm, err, w)
   265  		} else {
   266  			prettyPrint(vm, val, w)
   267  		}
   268  		fmt.Fprintln(w)
   269  	})
   270  	return fail
   271  }
   272  
   273  func (self *JSRE) Compile(filename string, src interface{}) (err error) {
   274  	self.Do(func(vm *otto.Otto) { _, err = compileAndRun(vm, filename, src) })
   275  	return err
   276  }
   277  
   278  func compileAndRun(vm *otto.Otto, filename string, src interface{}) (otto.Value, error) {
   279  	script, err := vm.Compile(filename, src)
   280  	if err != nil {
   281  		return otto.Value{}, err
   282  	}
   283  	return vm.Run(script)
   284  }