Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix for infinite recursion bug in overriding _.iteratee #2829

Merged
merged 2 commits into from
Mar 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions test/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -701,12 +701,12 @@

// Test custom iteratee
var builtinIteratee = _.iteratee;
_.iteratee = function(value) {
_.iteratee = function(value, context) {
// RegEx values return a function that returns the number of matches
if (_.isRegExp(value)) return function(obj) {
return (obj.match(value) || []).length;
};
return value;
return builtinIteratee(value, context);
};

var collection = ['foo', 'bar', 'bbiz'];
Expand Down Expand Up @@ -734,6 +734,17 @@
var objCollection = {a: 'foo', b: 'bar', c: 'bbiz'};
assert.deepEqual(_.mapObject(objCollection, /b/g), {a: 0, b: 1, c: 2});

// Ensure that the overridden iteratee can still fall back on the builtin
// iteratee.
assert.strictEqual(_.iteratee(), _.identity);
assert.deepEqual(_.toArray(_.iteratee(fn)(1, 2, 3)), _.range(1, 4));
var matcher = _.iteratee({b: 'bar'});
assert.equal(matcher(objCollection), true);
assert.equal(matcher({}), false);
var property = _.iteratee('b');
assert.equal(property(objCollection), 'bar');
assert.equal(property({}), undefined);

// Restore the builtin iteratee
_.iteratee = builtinIteratee;
});
Expand Down
16 changes: 10 additions & 6 deletions underscore.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,10 @@
};
};

var builtinIteratee;

// An internal function to generate callbacks that can be applied to each
// element in a collection, returning the desired result — either `identity`,
// an arbitrary callback, a property matcher, or a property accessor.
var cb = function(value, context, argCount) {
if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
var baseIteratee = function(value, context, argCount) {
if (value == null) return _.identity;
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
Expand All @@ -100,8 +97,15 @@
// External wrapper for our callback generator. Users may customize
// `_.iteratee` if they want additional predicate/iteratee shorthand styles.
// This abstraction hides the internal-only argCount argument.
_.iteratee = builtinIteratee = function(value, context) {
return cb(value, context, Infinity);
var exportIteratee = _.iteratee = function(value, context) {
return baseIteratee(value, context, Infinity);
};

// The function we actually call internally. It invokes _.iteratee if
// overridden, otherwise baseIteratee.
var cb = function(value, context, argCount) {
if (_.iteratee !== exportIteratee) return _.iteratee(value, context);
return baseIteratee(value, context, argCount);
};

// Some functions take a variable number of arguments, or a few expected
Expand Down