diff --git a/geth/jail/internal/timers/task.go b/geth/jail/internal/timers/task.go new file mode 100644 index 000000000..7de3b3528 --- /dev/null +++ b/geth/jail/internal/timers/task.go @@ -0,0 +1,54 @@ +package timers + +import ( + "time" + + "github.com/robertkrimen/otto" + "github.com/status-im/status-go/geth/jail/internal/loop" + "github.com/status-im/status-go/geth/jail/internal/vm" +) + +type timerTask struct { + id int64 + timer *time.Timer + duration time.Duration + interval bool + call otto.FunctionCall + stopped bool +} + +func (t *timerTask) SetID(id int64) { t.id = id } +func (t *timerTask) GetID() int64 { return t.id } + +func (t *timerTask) Execute(vm *vm.VM, l *loop.Loop) error { + arguments := t.getArguments() + if _, err := vm.Call(`Function.call.call`, nil, arguments...); err != nil { + return err + } + + if !(t.interval && !t.stopped) { + return nil + } + + t.timer.Reset(t.duration) + return l.Add(t) +} + +func (t *timerTask) Cancel() { + t.timer.Stop() +} + +func (t *timerTask) getArguments() (arguments []interface{}) { + arguments = make([]interface{}, 1) + if len(t.call.ArgumentList) > 2 { + tmp := t.call.ArgumentList[2:] + arguments = make([]interface{}, 2+len(tmp)) + + for i, value := range tmp { + arguments[i+2] = value + } + } + arguments[0] = t.call.ArgumentList[0] + + return +} diff --git a/geth/jail/internal/timers/timers.go b/geth/jail/internal/timers/timers.go index b8a4aad9c..5c00ac7fb 100644 --- a/geth/jail/internal/timers/timers.go +++ b/geth/jail/internal/timers/timers.go @@ -9,12 +9,7 @@ import ( "github.com/status-im/status-go/geth/jail/internal/vm" ) -var minDelay = map[bool]int64{ - true: 10, - false: 4, -} - -//Define jail timers +// Define jail timers func Define(vm *vm.VM, l *loop.Loop) error { if v, err := vm.Get("setTimeout"); err != nil { return err @@ -22,49 +17,67 @@ func Define(vm *vm.VM, l *loop.Loop) error { return nil } - newTimer := func(interval bool) func(call otto.FunctionCall) otto.Value { - return func(call otto.FunctionCall) otto.Value { - delay, _ := call.Argument(1).ToInteger() - if delay < minDelay[interval] { - delay = minDelay[interval] - } + timeHandlers := map[string]func(call otto.FunctionCall) otto.Value{ + "setInterval": newTimerHandler(l, true), + "setTimeout": newTimerHandler(l, false), + "setImmediate": newImmediateTimerHandler(l), + "clearTimeout": newClearTimeoutHandler(l), + "clearInterval": newClearTimeoutHandler(l), + "clearImmediate": newClearTimeoutHandler(l), + } - t := &timerTask{ - duration: time.Duration(delay) * time.Millisecond, - call: call, - interval: interval, - } - - // If err is non-nil, then the loop is closed and should not - // be used anymore. - if err := l.Add(t); err != nil { - return otto.UndefinedValue() - } - - t.timer = time.AfterFunc(t.duration, func() { - l.Ready(t) // nolint: errcheck - }) - - value, newTimerErr := call.Otto.ToValue(t) - if newTimerErr != nil { - panic(newTimerErr) - } - - return value + for k, handler := range timeHandlers { + if err := vm.Set(k, handler); err != nil { + return err } } - err := vm.Set("setTimeout", newTimer(false)) - if err != nil { - return err + return nil +} + +func getDelayWithMin(call otto.FunctionCall, interval bool) int64 { + var minDelay = map[bool]int64{ + true: 10, + false: 4, } - err = vm.Set("setInterval", newTimer(true)) - if err != nil { - return err + delay, _ := call.Argument(1).ToInteger() + if delay < minDelay[interval] { + return minDelay[interval] } + return delay +} - err = vm.Set("setImmediate", func(call otto.FunctionCall) otto.Value { +func newTimerHandler(l *loop.Loop, interval bool) func(call otto.FunctionCall) otto.Value { + return func(call otto.FunctionCall) otto.Value { + delay := getDelayWithMin(call, interval) + + t := &timerTask{ + duration: time.Duration(delay) * time.Millisecond, + call: call, + interval: interval, + } + // If err is non-nil, then the loop is closed and should not + // be used anymore. + if err := l.Add(t); err != nil { + return otto.UndefinedValue() + } + + t.timer = time.AfterFunc(t.duration, func() { + l.Ready(t) // nolint: errcheck + }) + + value, newTimerErr := call.Otto.ToValue(t) + if newTimerErr != nil { + panic(newTimerErr) + } + + return value + } +} + +func newImmediateTimerHandler(l *loop.Loop) func(call otto.FunctionCall) otto.Value { + return func(call otto.FunctionCall) otto.Value { t := &timerTask{ duration: time.Millisecond, call: call, @@ -81,17 +94,16 @@ func Define(vm *vm.VM, l *loop.Loop) error { }) value, setImmediateErr := call.Otto.ToValue(t) - if err != nil { + if setImmediateErr != nil { panic(setImmediateErr) } return value - }) - if err != nil { - return err } +} - clearTimeout := func(call otto.FunctionCall) otto.Value { +func newClearTimeoutHandler(l *loop.Loop) func(call otto.FunctionCall) otto.Value { + return func(call otto.FunctionCall) otto.Value { v, _ := call.Argument(0).Export() if t, ok := v.(*timerTask); ok { t.stopped = true @@ -101,62 +113,4 @@ func Define(vm *vm.VM, l *loop.Loop) error { return otto.UndefinedValue() } - err = vm.Set("clearTimeout", clearTimeout) - if err != nil { - return err - } - - err = vm.Set("clearInterval", clearTimeout) - if err != nil { - return err - } - - err = vm.Set("clearImmediate", clearTimeout) - return err -} - -type timerTask struct { - id int64 - timer *time.Timer - duration time.Duration - interval bool - call otto.FunctionCall - stopped bool -} - -func (t *timerTask) SetID(id int64) { t.id = id } -func (t *timerTask) GetID() int64 { return t.id } - -func (t *timerTask) Execute(vm *vm.VM, l *loop.Loop) error { - var arguments []interface{} - - if len(t.call.ArgumentList) > 2 { - tmp := t.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] = t.call.ArgumentList[0] - - if _, err := vm.Call(`Function.call.call`, nil, arguments...); err != nil { - return err - } - - if t.interval && !t.stopped { - t.timer.Reset(t.duration) - if err := l.Add(t); err != nil { - return err - } - } - - return nil -} - -func (t *timerTask) Cancel() { - t.timer.Stop() } diff --git a/geth/jail/internal/timers/timers_test.go b/geth/jail/internal/timers/timers_test.go index 17782bf05..85378ede3 100644 --- a/geth/jail/internal/timers/timers_test.go +++ b/geth/jail/internal/timers/timers_test.go @@ -91,6 +91,29 @@ func (s *TimersSuite) TestClearIntervalImmediately() { <-time.After(100 * time.Millisecond) } +func (s *TimersSuite) TestImmediateTimer() { + err := s.vm.Set("__done", func() { + s.ch <- struct{}{} + }) + s.NoError(err) + + err = s.loop.Eval(` + var v = setImmediate(function() { + __done(); + }); + `) + s.NoError(err) + + select { + case <-s.ch: + value, err := s.vm.Get("v") + s.NoError(err) + s.NotNil(value) + case <-time.After(100 * time.Millisecond): + s.Fail("test timed out") + } +} + type TimersSuite struct { suite.Suite