func prettyPrintJS(call otto.FunctionCall) otto.Value { for _, v := range call.ArgumentList { prettyPrint(call.Otto, v) fmt.Println() } return otto.UndefinedValue() }
// Exported functions always access the vm through the event queue. You can // call the functions of the otto vm directly to circumvent the queue. These // functions should be used if and only if running a routine that was already // called from JS through an RPC call. func (self *JSRE) runEventLoop() { vm := otto.New() registry := map[*jsTimer]*jsTimer{} ready := make(chan *jsTimer) newTimer := func(call otto.FunctionCall, interval bool) (*jsTimer, otto.Value) { delay, _ := call.Argument(1).ToInteger() if 0 >= delay { delay = 1 } timer := &jsTimer{ duration: time.Duration(delay) * time.Millisecond, call: call, interval: interval, } registry[timer] = timer timer.timer = time.AfterFunc(timer.duration, func() { ready <- timer }) value, err := call.Otto.ToValue(timer) if err != nil { panic(err) } return timer, value } setTimeout := func(call otto.FunctionCall) otto.Value { _, value := newTimer(call, false) return value } setInterval := func(call otto.FunctionCall) otto.Value { _, value := newTimer(call, true) return value } clearTimeout := func(call otto.FunctionCall) otto.Value { timer, _ := call.Argument(0).Export() if timer, ok := timer.(*jsTimer); ok { timer.timer.Stop() delete(registry, timer) } return otto.UndefinedValue() } vm.Set("_setTimeout", setTimeout) vm.Set("_setInterval", setInterval) vm.Run(`var setTimeout = function(args) { if (arguments.length < 1) { throw TypeError("Failed to execute 'setTimeout': 1 argument required, but only 0 present."); } return _setTimeout.apply(this, arguments); }`) vm.Run(`var setInterval = function(args) { if (arguments.length < 1) { throw TypeError("Failed to execute 'setInterval': 1 argument required, but only 0 present."); } return _setInterval.apply(this, arguments); }`) vm.Set("clearTimeout", clearTimeout) vm.Set("clearInterval", clearTimeout) var waitForCallbacks bool loop: for { select { case timer := <-ready: // execute callback, remove/reschedule the timer var arguments []interface{} if len(timer.call.ArgumentList) > 2 { tmp := timer.call.ArgumentList[2:] arguments = make([]interface{}, 2+len(tmp)) for i, value := range tmp { arguments[i+2] = value } } else { arguments = make([]interface{}, 1) } arguments[0] = timer.call.ArgumentList[0] _, err := vm.Call(`Function.call.call`, nil, arguments...) if err != nil { fmt.Println("js error:", err, arguments) } _, inreg := registry[timer] // when clearInterval is called from within the callback don't reset it if timer.interval && inreg { timer.timer.Reset(timer.duration) } else { delete(registry, timer) if waitForCallbacks && (len(registry) == 0) { break loop } } case req := <-self.evalQueue: // run the code, send the result back req.fn(vm) close(req.done) if waitForCallbacks && (len(registry) == 0) { break loop } case waitForCallbacks = <-self.stopEventLoop: if !waitForCallbacks || (len(registry) == 0) { break loop } } } for _, timer := range registry { timer.timer.Stop() delete(registry, timer) } self.loopWg.Done() }