Skip to content

Commit

Permalink
fix: avoid possible infinite loop by accessing observables in error h…
Browse files Browse the repository at this point in the history
…andler (#9489)
  • Loading branch information
ktsn authored and yyx990803 committed Feb 18, 2019
1 parent a702d19 commit ee29e41
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 12 deletions.
32 changes: 20 additions & 12 deletions src/core/util/error.js
Expand Up @@ -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 (
Expand Down
38 changes: 38 additions & 0 deletions test/unit/features/options/errorCaptured.spec.js
Expand Up @@ -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)
})
})

0 comments on commit ee29e41

Please sign in to comment.