Fix stack overflow when chaining an already chained wrapper#3021
Open
chatman-media wants to merge 1 commit into
Open
Fix stack overflow when chaining an already chained wrapper#3021chatman-media wants to merge 1 commit into
chatman-media wants to merge 1 commit into
Conversation
`_.chain(value).chain()` (or any redundant `.chain()` call in a chained sequence) overflowed the stack instead of behaving like a no-op. `chainResult` continued chaining via `_(obj).chain()`. When `obj` is the result of the standalone `chain` function it is already a wrapped instance, so `_(obj)` returns that same wrapper and invoking the prototype `chain` method re-enters `chainResult`, recursing without end. Call the standalone `chain` helper instead, which sets the `_chain` flag on the (possibly already wrapped) value directly and terminates. Refs jashkenas#2853.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #2853.
_.chain(value).chain()— or any redundant.chain()call within a chained sequence — overflows the stack withRangeError: Maximum call stack size exceededinstead of behaving like a no-op and returning a wrapper of the same value.Root cause
chainResultcontinues chaining intermediate results via_(obj).chain(). Whenobjis the value returned by the standalonechainfunction, it is already a wrapped_instance._(obj)therefore returns that same wrapper (perif (obj instanceof _) return obj;), and invoking the prototypechainmethod on it re-enterschainResult, which re-chains again — recursing without a base case until the stack overflows.The recursion loop is: prototype
chain→ standalonechain(produces a wrapper) →chainResultre-chains that wrapper via prototypechain→ repeat.As noted in the issue, this was introduced together with #1691.
Fix
Call the standalone
chainhelper fromchainResultinstead of_(obj).chain().chainsets the_chainflag on the (possibly already wrapped) value directly and returns, so chaining an already chained wrapper is idempotent and terminates:This is safe for every
chainResultcall site: the array-method wrappers pass a plain (unwrapped) value, andtapreturns the unwrapped value too — only_.chainreturns an_instance, which is exactly the case that previously recursed.Tests
Added a regression test in
test/chaining.js(#2853). It verifies the redundant.chain()no longer throws, preserves the wrapped value, stays chained, and that further chaining keeps working.Maximum call stack size exceeded(verified).209assertions) andeslintis clean.No public API change;
_.chain([1]).chain()now returns a chained wrapper of[1]as expected.