From dfbc27b0e4b2ee0d84f0d639faea31dd6175cd21 Mon Sep 17 00:00:00 2001
From: Herrington Darkholme <nonamesheep1@gmail.com>
Date: Wed, 28 Jun 2017 14:00:29 +0800
Subject: [PATCH] fix: ensure cleanup in watcher.get

watcher.get should always clean up observee stack in order to prevent memory leak. Also, non-user
defined watch should rethrow error.

#5975
---
 src/core/observer/watcher.js                | 27 +++++++++++----------
 test/unit/features/options/computed.spec.js | 11 +++++++++
 2 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/src/core/observer/watcher.js b/src/core/observer/watcher.js
index 3f3993df8be..40253149c34 100644
--- a/src/core/observer/watcher.js
+++ b/src/core/observer/watcher.js
@@ -94,22 +94,23 @@ export default class Watcher {
     pushTarget(this)
     let value
     const vm = this.vm
-    if (this.user) {
-      try {
-        value = this.getter.call(vm, vm)
-      } catch (e) {
+    try {
+      value = this.getter.call(vm, vm)
+    } catch (e) {
+      if (this.user) {
         handleError(e, vm, `getter for watcher "${this.expression}"`)
+      } else {
+        throw e
       }
-    } else {
-      value = this.getter.call(vm, vm)
-    }
-    // "touch" every property so they are all tracked as
-    // dependencies for deep watching
-    if (this.deep) {
-      traverse(value)
+    } finally {
+      // "touch" every property so they are all tracked as
+      // dependencies for deep watching
+      if (this.deep) {
+        traverse(value)
+      }
+      popTarget()
+      this.cleanupDeps()
     }
-    popTarget()
-    this.cleanupDeps()
     return value
   }
 
diff --git a/test/unit/features/options/computed.spec.js b/test/unit/features/options/computed.spec.js
index a48c7ed13b7..7bd0b151ac4 100644
--- a/test/unit/features/options/computed.spec.js
+++ b/test/unit/features/options/computed.spec.js
@@ -193,4 +193,15 @@ describe('Options computed', () => {
     })
     expect(`computed property "a" is already defined as a prop`).toHaveBeenWarned()
   })
+
+  it('rethrow computed error', () => {
+    const vm = new Vue({
+      computed: {
+        a: () => {
+          throw new Error('rethrow')
+        }
+      }
+    })
+    expect(() => vm.a).toThrowError('rethrow')
+  })
 })