fix: avoid possible infinite loop by accessing observables in error handler (#9489)

This commit is contained in:
katashin 2019-02-19 02:56:17 +08:00 committed by Evan You
parent a702d1947b
commit ee29e41ef4
2 changed files with 58 additions and 12 deletions

View File

@ -4,25 +4,33 @@ import config from '../config'
import { warn } from './debug'
import { inBrowser, inWeex } from './env'
import { isPromise } from 'shared/util'
import { pushTarget, popTarget } from '../observer/dep'
export function handleError (err: Error, vm: any, info: string) {
if (vm) {
let cur = vm
while ((cur = cur.$parent)) {
const hooks = cur.$options.errorCaptured
if (hooks) {
for (let i = 0; i < hooks.length; i++) {
try {
const capture = hooks[i].call(cur, err, vm, info) === false
if (capture) return
} catch (e) {
globalHandleError(e, cur, 'errorCaptured hook')
// Deactivate deps tracking while processing error handler to avoid possible infinite rendering.
// See: https://github.com/vuejs/vuex/issues/1505
pushTarget()
try {
if (vm) {
let cur = vm
while ((cur = cur.$parent)) {
const hooks = cur.$options.errorCaptured
if (hooks) {
for (let i = 0; i < hooks.length; i++) {
try {
const capture = hooks[i].call(cur, err, vm, info) === false
if (capture) return
} catch (e) {
globalHandleError(e, cur, 'errorCaptured hook')
}
}
}
}
}
globalHandleError(err, vm, info)
} finally {
popTarget()
}
globalHandleError(err, vm, info)
}
export function invokeWithErrorHandling (

View File

@ -209,4 +209,42 @@ describe('Options errorCaptured', () => {
expect(calls).toEqual([1, 2, 3])
})
// ref: https://github.com/vuejs/vuex/issues/1505
it('should not add watchers to render deps if they are referred from errorCaptured callback', done => {
const store = new Vue({
data: {
errors: []
}
})
const Child = {
computed: {
test() {
throw new Error('render error')
}
},
render(h) {
return h('div', {
attrs: {
'data-test': this.test
}
})
}
}
new Vue({
errorCaptured(error) {
store.errors.push(error)
},
render: h => h(Child)
}).$mount()
// Ensure not to trigger infinite loop
waitForUpdate(() => {
expect(store.errors.length).toBe(1)
expect(store.errors[0]).toEqual(new Error('render error'))
}).then(done)
})
})