Track less variables and optimize dead variable removal in build.js.

Former-commit-id: 926dae3b46fd491634559391c888fca8a83c84ac
This commit is contained in:
John-David Dalton
2013-06-16 14:32:52 -07:00
parent bbe1dfb53d
commit 2473e87947
3 changed files with 63 additions and 67 deletions

114
build.js
View File

@@ -664,7 +664,9 @@
* @returns {String} Returns the modified source. * @returns {String} Returns the modified source.
*/ */
function cleanupCompiled(source) { function cleanupCompiled(source) {
return source.replace(/([{}]) *;/g, '$1'); return source
.replace(/\b(function) *(\()/g, '$1$2')
.replace(/([{}]) *;/g, '$1');
} }
/** /**
@@ -978,32 +980,6 @@
) || methodName; ) || methodName;
} }
/**
* Gets the number of times a given variable is referenced in `source`.
*
* @private
* @param {String} source The source to process.
* @param {String} varName The name of the variable.
* @returns {Number} Returns the number of times `varName` is referenced.
*/
function getRefCount(source, varName) {
var indentA = isRemoved(source, 'runInContext') ? ' {2}' : ' {2,4}',
indentB = isRemoved(source, 'runInContext') ? ' {6}' : ' {6,8}',
snippet = source.replace(/^ *(?:\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/|\/\/.+)\n/gm, '');
var match = RegExp(
'^(' + indentA + ')var ' + varName + ' *(?:|= *(?:.+?(?:|&&\\n[^;]+)|(?:\\w+\\(|[{(]\\n)[\\s\\S]+?\\n\\1[^\\n ]+?));\\n|' +
'^' + indentA + 'var ' + varName + ' *=.+?,\\n(?= *\\w+ *=)|' +
'^' + indentB + varName + ' *=.+?[,;]\\n'
,'m')
.exec(snippet);
if (match) {
snippet = snippet.slice(0, match.index) + snippet.slice(match.index + match[0].length);
}
return _.size(match && snippet.match(RegExp('[^.\\w]' + varName + '\\b', 'g')));
}
/** /**
* Creates a sorted array of all variables defined outside of Lo-Dash methods. * Creates a sorted array of all variables defined outside of Lo-Dash methods.
* *
@@ -1018,7 +994,7 @@
result = []; result = [];
snippet.replace(RegExp( snippet.replace(RegExp(
'^(' + indentA + ')var (\\w+) *(?:|= *(?:.+?(?:|&&\\n[^;]+)|(?:\\w+\\(|[{(]\\n)[\\s\\S]+?\\n\\1[^\\n ]+?));\\n|' + '^(' + indentA + ')var (\\w+) *(?:|= *(?:.+?(?:|&&\\n[^;]+)|(?:\\w+\\(|[{[(]\\n)[\\s\\S]+?\\n\\1[^\\n ]+?));\\n|' +
'^' + indentA + 'var (\\w+) *=.+?,\\n(?= *\\w+ *=)|' + '^' + indentA + 'var (\\w+) *=.+?,\\n(?= *\\w+ *=)|' +
'^' + indentB + '(\\w+) *=.+?[,;]\\n' '^' + indentB + '(\\w+) *=.+?[,;]\\n'
,'gm'), function(match, indent, varA, varB, varC) { ,'gm'), function(match, indent, varA, varB, varC) {
@@ -1045,6 +1021,37 @@
}); });
} }
/**
* Determines if given variable is used in `source`.
*
* @private
* @param {String} source The source to process.
* @param {String} varName The name of the variable.
* @param {Boolean} [isShallow=false] A flag to indicate looking for varaibles one closure deep.
* @returns {Boolean} Returns `true` if the variable is used, else `false`.
*/
function isVarUsed(source, varName, isShallow) {
if (isShallow == null) {
isShallow = isRemoved(source, 'runInContext');
}
var indentA = isShallow ? ' {2}' : ' {2,4}',
indentB = isShallow ? ' {6}' : ' {6,8}',
snippet = source.replace(/^ *(?:\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/|\/\/.+)\n/gm, '');
var match = RegExp(
'^(' + indentA + ')var ' + varName + ' *(?:|= *(?:.+?(?:|&&\\n[^;]+)|(?:\\w+\\(|[{[(]\\n)[\\s\\S]+?\\n\\1[^\\n ]+?));\\n|' +
'^' + indentA + 'var ' + varName + ' *=.+?,\\n(?= *\\w+ *=)|' +
'^' + indentB + varName + ' *=.+?[,;]\\n'
, 'm')
.exec(snippet);
if (!match) {
return false;
}
snippet = snippet.slice(0, match.index) + snippet.slice(match.index + match[0].length);
return RegExp('[^.\\w"\']' + varName + '\\b').test(snippet);
}
/** /**
* Searches `source` for a `funcName` function declaration, expression, or * Searches `source` for a `funcName` function declaration, expression, or
* assignment and returns the matched snippet. * assignment and returns the matched snippet.
@@ -1068,7 +1075,7 @@
// match a function declaration // match a function declaration
'function ' + funcName + '\\b[\\s\\S]+?\\n\\1}|' + 'function ' + funcName + '\\b[\\s\\S]+?\\n\\1}|' +
// match a variable declaration with function expression // match a variable declaration with function expression
'var ' + funcName + ' *=.*?function[\\s\\S]+?\\n\\1}(?:\\(\\)\\))?;' + 'var ' + funcName + ' *=.*?function\\(.+?\{\\n[\\s\\S]+?\\n\\1}(?:\\(\\)\\))?;' +
// end non-capturing group // end non-capturing group
')\\n' ')\\n'
))); )));
@@ -1278,9 +1285,6 @@
* @returns {String} Returns the modified source. * @returns {String} Returns the modified source.
*/ */
function removeBindingOptimization(source) { function removeBindingOptimization(source) {
source = removeVar(source, 'fnToString');
source = removeVar(source, 'reThis');
// remove `reThis` from `createCallback` // remove `reThis` from `createCallback`
source = source.replace(matchFunction(source, 'createCallback'), function(match) { source = source.replace(matchFunction(source, 'createCallback'), function(match) {
return match.replace(/\s*\|\|\s*\(reThis[\s\S]+?\)\)\)/, ''); return match.replace(/\s*\|\|\s*\(reThis[\s\S]+?\)\)\)/, '');
@@ -1486,7 +1490,6 @@
function removeSupportNonEnumShadows(source) { function removeSupportNonEnumShadows(source) {
source = removeSupportProp(source, 'nonEnumShadows'); source = removeSupportProp(source, 'nonEnumShadows');
source = removeVar(source, 'nonEnumProps'); source = removeVar(source, 'nonEnumProps');
source = removeVar(source, 'shadowedProps');
source = removeFromCreateIterator(source, 'shadowedProps'); source = removeFromCreateIterator(source, 'shadowedProps');
// remove nested `nonEnumProps` assignments // remove nested `nonEnumProps` assignments
@@ -1577,8 +1580,6 @@
* @returns {String} Returns the modified source. * @returns {String} Returns the modified source.
*/ */
function removeRunInContext(source) { function removeRunInContext(source) {
source = removeVar(source, 'contextProps');
// replace reference in `reThis` assignment // replace reference in `reThis` assignment
source = source.replace(/\btest\(runInContext\)/, 'test(function() { return this; })'); source = source.replace(/\btest\(runInContext\)/, 'test(function() { return this; })');
@@ -1607,8 +1608,6 @@
* @returns {String} Returns the modified source. * @returns {String} Returns the modified source.
*/ */
function removeSetImmediate(source) { function removeSetImmediate(source) {
source = removeVar(source, 'setImmediate');
// remove the `setImmediate` fork of `_.defer`. // remove the `setImmediate` fork of `_.defer`.
source = source.replace(/(?:\s*\/\/.*)*\n( *)if *\(isV8 *&& *freeModule[\s\S]+?\n\1}/, ''); source = source.replace(/(?:\s*\/\/.*)*\n( *)if *\(isV8 *&& *freeModule[\s\S]+?\n\1}/, '');
@@ -1646,7 +1645,7 @@
function removeVar(source, varName) { function removeVar(source, varName) {
// simplify complex variable assignments // simplify complex variable assignments
if (/^(?:cloneableClasses|contextProps|ctorByClass|freeGlobal|nonEnumProps|shadowedProps|whitespace)$/.test(varName)) { if (/^(?:cloneableClasses|contextProps|ctorByClass|freeGlobal|nonEnumProps|shadowedProps|whitespace)$/.test(varName)) {
source = source.replace(RegExp('(var ' + varName + ' *=)[\\s\\S]+?[;}]\\n\\n'), '$1=null;\n\n'); source = source.replace(RegExp('(var ' + varName + ' *=)[\\s\\S]+?[;}]\\n\\n'), '$1 null;\n\n');
} }
source = removeFunction(source, varName); source = removeFunction(source, varName);
@@ -1654,7 +1653,7 @@
// match a variable declaration that's not part of a declaration list // match a variable declaration that's not part of a declaration list
source = source.replace(RegExp( source = source.replace(RegExp(
multilineComment + multilineComment +
'( *)var ' + varName + ' *(?:|= *(?:.+?(?:|&&\\n[^;]+)|(?:\\w+\\(|[{(]\\n)[\\s\\S]+?\\n\\1[^\\n ]+?));\\n' '( *)var ' + varName + ' *(?:|= *(?:.+?(?:|&&\\n[^;]+)|(?:\\w+\\(|[{[(]\\n)[\\s\\S]+?\\n\\1[^\\n ]+?));\\n'
), ''); ), '');
// match a variable declaration in a declaration list // match a variable declaration in a declaration list
@@ -2198,10 +2197,6 @@
source = removeSupportProp(source, 'fastBind'); source = removeSupportProp(source, 'fastBind');
source = replaceSupportProp(source, 'argsClass', 'false'); source = replaceSupportProp(source, 'argsClass', 'false');
_.each(['isIeOpera', 'isV8', 'getPrototypeOf', 'nativeBind', 'nativeCreate', 'nativeIsArray', 'nativeKeys', 'reNative'], function(varName) {
source = removeVar(source, varName);
});
// remove native `Function#bind` branch in `_.bind` // remove native `Function#bind` branch in `_.bind`
source = source.replace(matchFunction(source, 'bind'), function(match) { source = source.replace(matchFunction(source, 'bind'), function(match) {
return match.replace(/(?:\s*\/\/.*)*\s*return support\.fastBind[^:]+:\s*/, 'return '); return match.replace(/(?:\s*\/\/.*)*\s*return support\.fastBind[^:]+:\s*/, 'return ');
@@ -2367,7 +2362,7 @@
return match; return match;
} }
} }
return match.replace(/^(( *)if *\(.*?\bisArray\([^\)]+\).*?\) *{\n)(( *)var index[^;]+.+\n+)/m, function(snippet, statement, indent, vars) { return match.replace(/^(( *)if *\(.*?\bisArray\([^\)]+\).*?\) *\{\n)(( *)var index[^;]+.+\n+)/m, function(snippet, statement, indent, vars) {
vars = vars vars = vars
.replace(/\b(length *=)[^;=]+/, '$1 collection' + (methodName == 'reduce' ? '.length' : ' ? collection.length : 0')) .replace(/\b(length *=)[^;=]+/, '$1 collection' + (methodName == 'reduce' ? '.length' : ' ? collection.length : 0'))
.replace(RegExp('^ ' + indent, 'gm'), indent); .replace(RegExp('^ ' + indent, 'gm'), indent);
@@ -3028,11 +3023,6 @@
} }
}); });
// remove unneeded variables
if (!useLodashMethod('clone') && !useLodashMethod('cloneDeep')) {
source = removeVar(source, 'cloneableClasses');
source = removeVar(source, 'ctorByClass');
}
// remove `_.isEqual` use from `createCallback` // remove `_.isEqual` use from `createCallback`
if (!useLodashMethod('where')) { if (!useLodashMethod('where')) {
source = source.replace(matchFunction(source, 'createCallback'), function(match) { source = source.replace(matchFunction(source, 'createCallback'), function(match) {
@@ -3305,7 +3295,8 @@
// modify/remove references to removed methods/variables // modify/remove references to removed methods/variables
if (!isTemplate) { if (!isTemplate) {
if (isRemoved(source, 'clone')) { if (isRemoved(source, 'clone') ||
isUnderscore && (!useLodashMethod('clone') && !useLodashMethod('cloneDeep'))) {
source = removeVar(source, 'cloneableClasses'); source = removeVar(source, 'cloneableClasses');
source = removeVar(source, 'ctorByClass'); source = removeVar(source, 'ctorByClass');
} }
@@ -3465,21 +3456,26 @@
// remove unused variables // remove unused variables
(function() { (function() {
var varMap = {}, var useMap = {},
varNames = getVars(source); varNames = getVars(source),
isShallow = isRemoved(source, 'runInContext');
while (varNames.length) { while (varNames.length) {
varNames = _.sortBy(varNames, function(varName) { varNames = _.sortBy(varNames, function(varName) {
var count = getRefCount(source, varName); var result = isVarUsed(source, varName, isShallow);
varMap[varName] = count; useMap[varName] = result;
return count; return result;
}); });
var varName = varNames[0]; if (useMap[varNames[0]]) {
if (!varMap[varName]) { varNames.shift();
source = removeVar(source, varName); }
else {
while (!useMap[varNames[0]]) {
source = removeVar(source, varNames[0]);
varNames.shift();
}
} }
varNames.shift();
} }
}()); }());
} }

10
dist/lodash.js vendored
View File

@@ -863,7 +863,7 @@
* @param {Object} object The object to inspect. * @param {Object} object The object to inspect.
* @returns {Array} Returns a new array of property names. * @returns {Array} Returns a new array of property names.
*/ */
var shimKeys = function (object) { var shimKeys = function(object) {
var index, iterable = object, result = []; var index, iterable = object, result = [];
if (!iterable) return result; if (!iterable) return result;
if (!(objectTypes[typeof object])) return result; if (!(objectTypes[typeof object])) return result;
@@ -946,7 +946,7 @@
* defaults(food, { 'name': 'banana', 'type': 'fruit' }); * defaults(food, { 'name': 'banana', 'type': 'fruit' });
* // => { 'name': 'apple', 'type': 'fruit' } * // => { 'name': 'apple', 'type': 'fruit' }
*/ */
var assign = function (object, source, guard) { var assign = function(object, source, guard) {
var index, iterable = object, result = iterable; var index, iterable = object, result = iterable;
if (!iterable) return result; if (!iterable) return result;
var args = arguments, var args = arguments,
@@ -1169,7 +1169,7 @@
* _.defaults(food, { 'name': 'banana', 'type': 'fruit' }); * _.defaults(food, { 'name': 'banana', 'type': 'fruit' });
* // => { 'name': 'apple', 'type': 'fruit' } * // => { 'name': 'apple', 'type': 'fruit' }
*/ */
var defaults = function (object, source, guard) { var defaults = function(object, source, guard) {
var index, iterable = object, result = iterable; var index, iterable = object, result = iterable;
if (!iterable) return result; if (!iterable) return result;
var args = arguments, var args = arguments,
@@ -1252,7 +1252,7 @@
* }); * });
* // => alerts 'name' and 'bark' (order is not guaranteed) * // => alerts 'name' and 'bark' (order is not guaranteed)
*/ */
var forIn = function (collection, callback, thisArg) { var forIn = function(collection, callback, thisArg) {
var index, iterable = collection, result = iterable; var index, iterable = collection, result = iterable;
if (!iterable) return result; if (!iterable) return result;
if (!objectTypes[typeof iterable]) return result; if (!objectTypes[typeof iterable]) return result;
@@ -1284,7 +1284,7 @@
* }); * });
* // => alerts '0', '1', and 'length' (order is not guaranteed) * // => alerts '0', '1', and 'length' (order is not guaranteed)
*/ */
var forOwn = function (collection, callback, thisArg) { var forOwn = function(collection, callback, thisArg) {
var index, iterable = collection, result = iterable; var index, iterable = collection, result = iterable;
if (!iterable) return result; if (!iterable) return result;
if (!objectTypes[typeof iterable]) return result; if (!objectTypes[typeof iterable]) return result;

View File

@@ -538,7 +538,7 @@
* @param {Object} object The object to inspect. * @param {Object} object The object to inspect.
* @returns {Array} Returns a new array of property names. * @returns {Array} Returns a new array of property names.
*/ */
var shimKeys = function (object) { var shimKeys = function(object) {
var index, iterable = object, result = []; var index, iterable = object, result = [];
if (!iterable) return result; if (!iterable) return result;
if (!(objectTypes[typeof object])) return result; if (!(objectTypes[typeof object])) return result;
@@ -750,7 +750,7 @@
* }); * });
* // => alerts 'name' and 'bark' (order is not guaranteed) * // => alerts 'name' and 'bark' (order is not guaranteed)
*/ */
var forIn = function (collection, callback) { var forIn = function(collection, callback) {
var index, iterable = collection, result = iterable; var index, iterable = collection, result = iterable;
if (!iterable) return result; if (!iterable) return result;
if (!objectTypes[typeof iterable]) return result; if (!objectTypes[typeof iterable]) return result;
@@ -781,7 +781,7 @@
* }); * });
* // => alerts '0', '1', and 'length' (order is not guaranteed) * // => alerts '0', '1', and 'length' (order is not guaranteed)
*/ */
var forOwn = function (collection, callback) { var forOwn = function(collection, callback) {
var index, iterable = collection, result = iterable; var index, iterable = collection, result = iterable;
if (!iterable) return result; if (!iterable) return result;
if (!objectTypes[typeof iterable]) return result; if (!objectTypes[typeof iterable]) return result;