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 }