[#516] Fix panic based on wrong error in setImmediate function (#535)

Additionally, this change clean ups and improves organization of `geth/jail/internal/timers` package.
This commit is contained in:
Adrià Cidre 2018-01-10 10:42:40 +01:00 committed by Adam Babik
parent 83415ea4c8
commit 7ab6a062ec
3 changed files with 135 additions and 104 deletions

View File

@ -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
}

View File

@ -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()
}

View File

@ -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