# JS: Call stack size exceeded

Some time ago I was working with big arrays of about 2.000.000 elements. One of the operations was to find the maximum value in the array.

Sounds simple, right? We can use Max.max:

```
const arr = new Array(2000000);
...
const max = Math.max(...arr);
```

However, if you try to execute it, you will have an error:

Looks weird... Probably the issue is with the spread operator?

No, it's not. So why do we have such an error?

The first thing I was thinking of is that the Math.max uses recursion.

However, it looks weird, that v8 developers could use recursion for that. Let's check it in v8 sources: https://github.com/v8/v8/blob/dc712da548c7fb433caed56af9a021d964952728/src/builtins/math.tq#L134-L144

```
// ES6 #sec-math.max
extern macro Float64Max(float64, float64): float64;
transitioning javascript builtin
MathMax(js-implicit context: NativeContext)(...arguments): Number {
let result: float64 = MINUS_V8_INFINITY;
const argCount = arguments.length;
for (let i: intptr = 0; i < argCount; i++) {
const doubleValue = TruncateTaggedToFloat64(arguments[i]);
result = Float64Max(result, doubleValue);
}
return Convert<Number>(result);
}
```

Of course, we have recursion here, but the depth of it is only 2 functions. The first one is MathMax and the second one compares exactly 2 values.

Maybe I have an old v8 version? We can dig into 2014-2015 chrome versions: https://chromium.googlesource.com/v8/v8/+/4.3.21/src/math.js?autodive=0%2F%2F#86 and we can see that the old implementation doesn't have recursion:

```
function MathMax(arg1, arg2) { // length == 2
var length = %_ArgumentsLength();
if (length == 2) {
arg1 = TO_NUMBER_INLINE(arg1);
arg2 = TO_NUMBER_INLINE(arg2);
if (arg2 > arg1) return arg2;
if (arg1 > arg2) return arg1;
if (arg1 == arg2) {
// Make sure -0 is considered less than +0.
return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
}
// All comparisons failed, one of the arguments must be NaN.
return NAN;
}
var r = -INFINITY;
for (var i = 0; i < length; i++) {
var n = %_Arguments(i);
if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
// Make sure +0 is considered greater than -0.
if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) {
r = n;
}
}
return r;
}
```

## So, what was the problem?

To find this out we can use the best tool which developers have. Let's take a long patient look once again:

```
const arr = new Array(2000000);
...
const max = Math.max(...arr);
```

How do we call this function?

When we call a function that has 2 Number arguments:

```
function test(a, b) {
}
test(1,2);
```

The arguments, which are primitive types, are transmitted by their values. We will write to the call stack 2 numbers: 1 and 2.

Now, let's call the function using the reference (object) type:

```
function test(a, b) {
}
test(new Array(2000000));
```

In this case, we create an array for 2.000.000 elements. Array elements are stored in the heap, and therefore the function receives only the reference to the array.

*If you're interested in getting more information about Primitive and Reference types you can check this article:* *dev.to/xnimorz/javascript-memory-management..*

📝 When we use apply or spread operator javascript converts each element from your Iterable object to a separate argument. And therefore you may pass not a reference, but a lot of primitive values.

When we call this code:

In the third example, we transmit a million numbers as primitives to our test function. We try to write every single array element to the call stack. More info about the call stack: https://en.wikipedia.org/wiki/Call_stack#Structure

It's how we exceeded the call stack limit. Be careful with the spread operator 🙈