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