Skip to content

Avoid RangeError in arrayBind foreign implementation #314

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged

Conversation

pete-murphy
Copy link
Contributor

@pete-murphy pete-murphy commented Feb 10, 2025

Description of the change

Fixes #309. See the related discussion on Discourse.

  • ec814e4 adds a failing test, reproducing the issue by passing 106,000 arguments to Array.prototype.push.apply
  • a7159c7 adds a case to use flatMap if available in the arrayBind implementation
  • e9ad550 simplifies the other case, and makes it stack-safe but gives up the Array.prototype.push.apply optimization

I don't know the motivation behind that optimization, so I am likely not aware of the tradeoffs. I tried some benchmarks in various JS run times, not seeing wildly-different results between existing solution (#ff725c), flatMap (#4269d0), and simplified version added in e9ad550 (#efb118):

Multiple line charts showing benchmark results for three alternative implementations of arrayBind in four different JS runtimes: Safari, Firefox, Node, and Chrome

See also #311


Checklist:

  • Added the change to the changelog's "Unreleased" section with a reference to this PR (e.g. "- Made a change (#0000)")
  • Linked any existing issues or proposals that this pull request should close
  • Updated or added relevant documentation
  • Added a test for the contribution (if applicable)

Demonstrating that the current implementation of `Array`'s `Bind` instance
causes `RangeError: Maximum call stack size exceeded` when the output of `f` in
`ma >>= f` is sufficiently large.

This is due to usage of `Function.prototype.apply`. From
[MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#using_apply_and_built-in_functions):

> But beware: by using apply() (or the spread syntax) with an arbitrarily long
arguments list, you run the risk of exceeding the JavaScript engine's argument
length limit.

> The consequences of calling a function with too many arguments (that is, more
than tens of thousands of arguments) is unspecified and varies across engines.
(The JavaScriptCore engine has a hard-coded [argument limit of
65536](https://webkit.org/b/80797).)

Node v20.18.1 seems to have a higher limit around 106,000.
Using static check to determine if `Array.prototype.flatMap` is available, and
use `var` instead of `let` in for loop to match existing code style.
@pete-murphy pete-murphy force-pushed the pete/309-array-bind-argument-limit branch from f878325 to fbb41ce Compare February 15, 2025 00:33
@natefaubion natefaubion merged commit 2a51e60 into purescript:master Feb 15, 2025
1 check passed
@pete-murphy pete-murphy deleted the pete/309-array-bind-argument-limit branch February 21, 2025 15:54
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

arrayBind causes Maximum call stack size exceeded
3 participants