Drop funcDecomp optimization in baseCallback and ensure shortcut fusion for _.flow and _.flowRight works in minified builds.

This commit is contained in:
jdalton
2015-03-19 08:37:22 -07:00
parent 462c482873
commit 94ca50883f
2 changed files with 67 additions and 98 deletions

View File

@@ -120,9 +120,6 @@
var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g, var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g,
reHasRegExpChars = RegExp(reRegExpChars.source); reHasRegExpChars = RegExp(reRegExpChars.source);
/** Used to detect functions containing a `this` reference. */
var reThis = /\bthis\b/;
/** Used to match unescaped characters in compiled string literals. */ /** Used to match unescaped characters in compiled string literals. */
var reUnescapedString = /['\n\r\u2028\u2029\\]/g; var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
@@ -153,7 +150,7 @@
'Object', 'RegExp', 'Set', 'String', '_', 'clearTimeout', 'document', 'Object', 'RegExp', 'Set', 'String', '_', 'clearTimeout', 'document',
'isFinite', 'parseInt', 'setTimeout', 'TypeError', 'Uint8Array', 'isFinite', 'parseInt', 'setTimeout', 'TypeError', 'Uint8Array',
'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap',
'window', 'WinRTError' 'window'
]; ];
/** Used to fix the JScript `[[DontEnum]]` bug. */ /** Used to fix the JScript `[[DontEnum]]` bug. */
@@ -831,6 +828,9 @@
/** Used to store function metadata. */ /** Used to store function metadata. */
var metaMap = WeakMap && new WeakMap; var metaMap = WeakMap && new WeakMap;
/** Used to lookup unminified function names. */
var realNames = {};
/** Used to lookup a type array constructors by `toStringTag`. */ /** Used to lookup a type array constructors by `toStringTag`. */
var ctorByTag = {}; var ctorByTag = {};
ctorByTag[float32Tag] = context.Float32Array; ctorByTag[float32Tag] = context.Float32Array;
@@ -1046,7 +1046,7 @@
* @memberOf _.support * @memberOf _.support
* @type boolean * @type boolean
*/ */
support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext); support.funcDecomp = /\bthis\b/.test(function() { return this; });
/** /**
* Detect if `Function#name` is supported (all but IE). * Detect if `Function#name` is supported (all but IE).
@@ -1846,9 +1846,9 @@
function baseCallback(func, thisArg, argCount) { function baseCallback(func, thisArg, argCount) {
var type = typeof func; var type = typeof func;
if (type == 'function') { if (type == 'function') {
return (typeof thisArg != 'undefined' && isBindable(func)) return typeof thisArg == 'undefined'
? bindCallback(func, thisArg, argCount) ? func
: func; : bindCallback(func, thisArg, argCount);
} }
if (func == null) { if (func == null) {
return identity; return identity;
@@ -3379,29 +3379,37 @@
if (!length) { if (!length) {
return function() { return arguments[0]; }; return function() { return arguments[0]; };
} }
var index = fromRight ? length : -1, var wrapper,
index = fromRight ? length : -1,
leftIndex = 0, leftIndex = 0,
funcs = Array(length), funcs = Array(length);
wrapper = new LodashWrapper([]);
while ((fromRight ? index-- : ++index < length)) { while ((fromRight ? index-- : ++index < length)) {
var func = arguments[index]; var func = funcs[leftIndex++] = arguments[index];
if (typeof func != 'function') { if (typeof func != 'function') {
throw new TypeError(FUNC_ERROR_TEXT); throw new TypeError(FUNC_ERROR_TEXT);
} }
var data = func.name === 'wrapper' && getData(func); var funcName = wrapper ? null : getFuncName(func);
if (data && data !== true && isLaziable(data[0])) { if (funcName && (funcName == 'wrapper' || isLaziable(func))) {
wrapper = wrapper[data[0].name].apply(wrapper, data[3]); wrapper = new LodashWrapper([]);
} else if (func.length == 1 && isLaziable(func)) { }
wrapper = wrapper[func.name](); }
} else { index = wrapper ? -1 : length;
wrapper = wrapper.thru(func); while (++index < length) {
func = funcs[index];
var funcName = getFuncName(func),
data = funcName == 'wrapper' ? getData(func) : null;
if (data && isLaziable(data[0])) {
wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);
} else {
wrapper = (func.length == 1 && isLaziable(func)) ? wrapper[funcName]() : wrapper.thru(func);
} }
funcs[leftIndex++] = func;
} }
return function() { return function() {
var args = arguments; var args = arguments;
if (args.length == 1 && isArray(args[0])) { if (wrapper && args.length == 1 && isArray(args[0])) {
return wrapper.plant(args[0]).value(); return wrapper.plant(args[0]).value();
} }
var index = 0, var index = 0,
@@ -3699,10 +3707,10 @@
partials = holders = null; partials = holders = null;
} }
var data = !isBindKey && getData(func), var data = isBindKey ? null : getData(func),
newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity]; newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
if (data && data !== true) { if (data) {
mergeData(newData, data); mergeData(newData, data);
bitmask = newData[1]; bitmask = newData[1];
arity = newData[9]; arity = newData[9];
@@ -4055,31 +4063,6 @@
return result; return result;
} }
/**
* Checks if `func` is eligible for `this` binding.
*
* @private
* @param {Function} func The function to check.
* @returns {boolean} Returns `true` if `func` is eligible, else `false`.
*/
function isBindable(func) {
var support = lodash.support,
result = !(support.funcNames ? func.name : support.funcDecomp);
if (!result) {
var source = fnToString.call(func);
if (!support.funcNames) {
result = !reFuncName.test(source);
}
if (!result) {
// Check if `func` references the `this` keyword and store the result.
result = reThis.test(source) || isNative(func);
baseSetData(func, result);
}
}
return result;
}
/** /**
* Checks if `value` is a valid array-like index. * Checks if `value` is a valid array-like index.
* *
@@ -4121,9 +4104,28 @@
return false; return false;
} }
var getFuncName = !support.funcNames ? constant('') : function(func) {
var result = func.name;
if (result) {
return realNames[result] || result;
}
var anons = realNames[result];
if (!anons) {
return result;
}
var length = anons.length;
while (length--) {
var anon = anons[length];
if (anon.func == func) {
return anon.name;
}
}
return result;
};
function isLaziable(func) { function isLaziable(func) {
var funcName = support.funcNames ? func.name : ''; var funcName = getFuncName(func);
return (funcName && func === lodash[funcName] && funcName in LazyWrapper.prototype) || false; return funcName ? func === lodash[funcName] : false;
} }
/** /**
@@ -11942,6 +11944,20 @@
}; };
}); });
if (support.funcNames) {
realNames[createHybridWrapper(null, BIND_KEY_FLAG).name] = 'wrapper';
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
var lodashFunc = lodash[methodName],
key = lodashFunc.name;
if (key) {
realNames[key] = methodName;
} else {
var anons = realNames[key] || (realNames[key] = []);
anons.push({ 'name': methodName, 'func': lodashFunc });
}
});
}
// Add functions to the lazy wrapper. // Add functions to the lazy wrapper.
LazyWrapper.prototype.clone = lazyClone; LazyWrapper.prototype.clone = lazyClone;
LazyWrapper.prototype.reverse = lazyReverse; LazyWrapper.prototype.reverse = lazyReverse;

View File

@@ -2233,7 +2233,7 @@
}); });
test('`_.' + methodName + '` should support shortcut fusion', 3, function() { test('`_.' + methodName + '` should support shortcut fusion', 3, function() {
if (!isNpm) { if (!isNpm && _.support.funcNames) {
var filterCount = 0, var filterCount = 0,
mapCount = 0; mapCount = 0;
@@ -2625,53 +2625,6 @@
} }
}); });
test('should return the function provided when there is no `this` reference', 2, function() {
function a() {}
function b() { return this.b; }
var object = {};
if (_.support.funcDecomp) {
strictEqual(_.callback(a, object), a);
notStrictEqual(_.callback(b, object), b);
}
else {
skipTest(2);
}
});
test('should work with bizarro `_.support.funcNames`', 6, function() {
function a() {}
var b = function() {};
function c() {
return this;
}
var object = {},
supportBizarro = lodashBizarro ? lodashBizarro.support : {},
funcDecomp = supportBizarro.funcDecomp,
funcNames = supportBizarro.funcNames;
supportBizarro.funcNames = !supportBizarro.funcNames;
supportBizarro.funcDecomp = true;
_.each([a, b, c], function(fn) {
if (lodashBizarro && _.support.funcDecomp) {
var callback = lodashBizarro.callback(fn, object);
strictEqual(callback(), fn === c ? object : undefined);
strictEqual(callback === fn, _.support.funcNames && fn === a);
}
else {
skipTest(2);
}
});
supportBizarro.funcDecomp = funcDecomp;
supportBizarro.funcNames = funcNames;
});
test('should work as an iteratee for methods like `_.map`', 1, function() { test('should work as an iteratee for methods like `_.map`', 1, function() {
var fn = function() { return this instanceof Number; }, var fn = function() { return this instanceof Number; },
array = [fn, fn, fn], array = [fn, fn, fn],