Skip to content

Commit ee29e41

Browse files
ktsnyyx990803
authored andcommitted
fix: avoid possible infinite loop by accessing observables in error handler (#9489)
1 parent a702d19 commit ee29e41

File tree

2 files changed

+58
-12
lines changed

2 files changed

+58
-12
lines changed

src/core/util/error.js

+20-12
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,33 @@ import config from '../config'
44
import { warn } from './debug'
55
import { inBrowser, inWeex } from './env'
66
import { isPromise } from 'shared/util'
7+
import { pushTarget, popTarget } from '../observer/dep'
78

89
export function handleError (err: Error, vm: any, info: string) {
9-
if (vm) {
10-
let cur = vm
11-
while ((cur = cur.$parent)) {
12-
const hooks = cur.$options.errorCaptured
13-
if (hooks) {
14-
for (let i = 0; i < hooks.length; i++) {
15-
try {
16-
const capture = hooks[i].call(cur, err, vm, info) === false
17-
if (capture) return
18-
} catch (e) {
19-
globalHandleError(e, cur, 'errorCaptured hook')
10+
// Deactivate deps tracking while processing error handler to avoid possible infinite rendering.
11+
// See: https://github.com/vuejs/vuex/issues/1505
12+
pushTarget()
13+
try {
14+
if (vm) {
15+
let cur = vm
16+
while ((cur = cur.$parent)) {
17+
const hooks = cur.$options.errorCaptured
18+
if (hooks) {
19+
for (let i = 0; i < hooks.length; i++) {
20+
try {
21+
const capture = hooks[i].call(cur, err, vm, info) === false
22+
if (capture) return
23+
} catch (e) {
24+
globalHandleError(e, cur, 'errorCaptured hook')
25+
}
2026
}
2127
}
2228
}
2329
}
30+
globalHandleError(err, vm, info)
31+
} finally {
32+
popTarget()
2433
}
25-
globalHandleError(err, vm, info)
2634
}
2735

2836
export function invokeWithErrorHandling (

test/unit/features/options/errorCaptured.spec.js

+38
Original file line numberDiff line numberDiff line change
@@ -209,4 +209,42 @@ describe('Options errorCaptured', () => {
209209

210210
expect(calls).toEqual([1, 2, 3])
211211
})
212+
213+
// ref: https://github.com/vuejs/vuex/issues/1505
214+
it('should not add watchers to render deps if they are referred from errorCaptured callback', done => {
215+
const store = new Vue({
216+
data: {
217+
errors: []
218+
}
219+
})
220+
221+
const Child = {
222+
computed: {
223+
test() {
224+
throw new Error('render error')
225+
}
226+
},
227+
228+
render(h) {
229+
return h('div', {
230+
attrs: {
231+
'data-test': this.test
232+
}
233+
})
234+
}
235+
}
236+
237+
new Vue({
238+
errorCaptured(error) {
239+
store.errors.push(error)
240+
},
241+
render: h => h(Child)
242+
}).$mount()
243+
244+
// Ensure not to trigger infinite loop
245+
waitForUpdate(() => {
246+
expect(store.errors.length).toBe(1)
247+
expect(store.errors[0]).toEqual(new Error('render error'))
248+
}).then(done)
249+
})
212250
})

0 commit comments

Comments
 (0)