Browse Source

Misc - Fix evaluatorMemo memory leak (#2398)

* Add manual test

* Fix memory leak in evaluatorMemo

* Amend html in manual test
Simone Todaro 3 years ago
parent
commit
c381571e5a
2 changed files with 35 additions and 0 deletions
  1. 7 0
      packages/alpinejs/src/evaluator.js
  2. 28 0
      tests/cypress/manual-memory.html

+ 7 - 0
packages/alpinejs/src/evaluator.js

@@ -97,11 +97,18 @@ function generateEvaluatorFromString(dataStack, expression, el) {
             if (func.finished) {
                 // Return the immediate result.
                 runIfTypeOfFunction(receiver, func.result, completeScope, params, el)
+                // Once the function has run, we clear func.result so we don't create
+                // memory leaks. func is stored in the evaluatorMemo and every time
+                // it runs, it assigns the evaluated expression to result which could
+                // potentially store reference to DOM element that will be removed
+                // later on
+                func.result = undefined
             } else {
                 // If not, return the result when the promise resolves.
                 promise.then(result => {
                     runIfTypeOfFunction(receiver, result, completeScope, params, el)
                 }).catch( error => handleError( error, el, expression ) )
+                .finally( () => func.result = undefined )
             }
         }
     }

+ 28 - 0
tests/cypress/manual-memory.html

@@ -0,0 +1,28 @@
+<html>
+    <script src="/../../packages/alpinejs/dist/cdn.js" defer></script>
+    <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
+
+    <table class="w-dull">
+        <tr>
+            <td class="p-10 w-1/3"><code>MemoEvaluator cache</code></td>
+            <td class="p-10 w-1/3">
+                <p>
+                    In chrome, open dev tools > Memory tag, click on
+                    "Take heap snapshot". First of all, check that there are not existing leaks affecting the test results clicking on
+                    "Take heap snapshot": it should not found any results;
+                    if it does, you might want to run the test in incognito
+                    mode so it does not load any chrome extensions).
+                    Once verified, click the "test" button, go to the memory
+                    tab, click "Collect garbage" a couple of times, take another snapshot and verify we don't have any new
+                    detached node in memory.
+                </p>
+            </td>
+            <td class="p-10 w-1/3">
+                <div id="one" x-data="{ foo: 'bar' }">
+                    <span x-text="foo"></span>
+                    <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" @click="document.getElementById('one').remove()">Test</button>
+                </div>
+            </td>
+        </tr>
+    </table>
+</html>