async-max-frame-count.js (2979B)
1 // Test that async stacks are limited on recursion. 2 3 const defaultAsyncStackLimit = 60; 4 5 function recur(n, limit) { 6 if (n > 0) { 7 return callFunctionWithAsyncStack(function recur() {return recur(n - 1, limit)}, 8 saveStack(limit), "Recurse"); 9 } 10 return saveStack(limit); 11 } 12 13 function checkRecursion(n, limit) { 14 print("checkRecursion(" + String(n) + ", " + String(limit) + ")"); 15 16 try { 17 var stack = recur(n, limit); 18 } catch (e) { 19 // Some platforms, like ASAN builds, can end up overrecursing. Tolerate 20 // these failures. 21 assertEq(/too much recursion/.test("" + e), true); 22 return; 23 } 24 25 // Async stacks are limited even if we didn't ask for a limit. There is a 26 // default limit on frames attached on top of any synchronous frames, and 27 // every time the limit is reached when capturing, half of the frames are 28 // truncated from the old end of the async stack. 29 if (limit == 0) { 30 // Always add one synchronous frame that is the last call to `recur`. 31 if (n + 1 < defaultAsyncStackLimit) { 32 limit = defaultAsyncStackLimit + 1; 33 } else { 34 limit = n + 2 - (defaultAsyncStackLimit / 2); 35 } 36 } 37 38 // The first `n` or `limit` frames should have `recur` as their `asyncParent`. 39 for (var i = 0; i < Math.min(n, limit); i++) { 40 assertEq(stack.functionDisplayName, "recur"); 41 assertEq(stack.parent, null); 42 stack = stack.asyncParent; 43 } 44 45 // This frame should be the first call to `recur`. 46 if (limit > n) { 47 assertEq(stack.functionDisplayName, "recur"); 48 assertEq(stack.asyncParent, null); 49 stack = stack.parent; 50 } else { 51 assertEq(stack, null); 52 } 53 54 // This frame should be the call to `checkRecursion`. 55 if (limit > n + 1) { 56 assertEq(stack.functionDisplayName, "checkRecursion"); 57 assertEq(stack.asyncParent, null); 58 stack = stack.parent; 59 } else { 60 assertEq(stack, null); 61 } 62 63 // We should be at the top frame, which is the test script itself. 64 if (limit > n + 2) { 65 assertEq(stack.functionDisplayName, null); 66 assertEq(stack.asyncParent, null); 67 assertEq(stack.parent, null); 68 } else { 69 assertEq(stack, null); 70 } 71 } 72 73 // Check capturing with no limit, which should still apply a default limit. 74 checkRecursion(0, 0); 75 checkRecursion(1, 0); 76 checkRecursion(2, 0); 77 checkRecursion(defaultAsyncStackLimit - 10, 0); 78 checkRecursion(defaultAsyncStackLimit, 0); 79 checkRecursion(defaultAsyncStackLimit + 10, 0); 80 81 // Limit of 1 frame. 82 checkRecursion(0, 1); 83 checkRecursion(1, 1); 84 checkRecursion(2, 1); 85 86 // Limit of 2 frames. 87 checkRecursion(0, 2); 88 checkRecursion(1, 2); 89 checkRecursion(2, 2); 90 91 // Limit of 3 frames. 92 checkRecursion(0, 3); 93 checkRecursion(1, 3); 94 checkRecursion(2, 3); 95 96 // Limit higher than the default limit. 97 checkRecursion(defaultAsyncStackLimit + 10, defaultAsyncStackLimit + 10); 98 checkRecursion(defaultAsyncStackLimit + 11, defaultAsyncStackLimit + 10); 99 checkRecursion(defaultAsyncStackLimit + 12, defaultAsyncStackLimit + 10);