time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

_baseConvert.js (16414B)


      1 var mapping = require('./_mapping'),
      2     fallbackHolder = require('./placeholder');
      3 
      4 /** Built-in value reference. */
      5 var push = Array.prototype.push;
      6 
      7 /**
      8  * Creates a function, with an arity of `n`, that invokes `func` with the
      9  * arguments it receives.
     10  *
     11  * @private
     12  * @param {Function} func The function to wrap.
     13  * @param {number} n The arity of the new function.
     14  * @returns {Function} Returns the new function.
     15  */
     16 function baseArity(func, n) {
     17   return n == 2
     18     ? function(a, b) { return func.apply(undefined, arguments); }
     19     : function(a) { return func.apply(undefined, arguments); };
     20 }
     21 
     22 /**
     23  * Creates a function that invokes `func`, with up to `n` arguments, ignoring
     24  * any additional arguments.
     25  *
     26  * @private
     27  * @param {Function} func The function to cap arguments for.
     28  * @param {number} n The arity cap.
     29  * @returns {Function} Returns the new function.
     30  */
     31 function baseAry(func, n) {
     32   return n == 2
     33     ? function(a, b) { return func(a, b); }
     34     : function(a) { return func(a); };
     35 }
     36 
     37 /**
     38  * Creates a clone of `array`.
     39  *
     40  * @private
     41  * @param {Array} array The array to clone.
     42  * @returns {Array} Returns the cloned array.
     43  */
     44 function cloneArray(array) {
     45   var length = array ? array.length : 0,
     46       result = Array(length);
     47 
     48   while (length--) {
     49     result[length] = array[length];
     50   }
     51   return result;
     52 }
     53 
     54 /**
     55  * Creates a function that clones a given object using the assignment `func`.
     56  *
     57  * @private
     58  * @param {Function} func The assignment function.
     59  * @returns {Function} Returns the new cloner function.
     60  */
     61 function createCloner(func) {
     62   return function(object) {
     63     return func({}, object);
     64   };
     65 }
     66 
     67 /**
     68  * A specialized version of `_.spread` which flattens the spread array into
     69  * the arguments of the invoked `func`.
     70  *
     71  * @private
     72  * @param {Function} func The function to spread arguments over.
     73  * @param {number} start The start position of the spread.
     74  * @returns {Function} Returns the new function.
     75  */
     76 function flatSpread(func, start) {
     77   return function() {
     78     var length = arguments.length,
     79         lastIndex = length - 1,
     80         args = Array(length);
     81 
     82     while (length--) {
     83       args[length] = arguments[length];
     84     }
     85     var array = args[start],
     86         otherArgs = args.slice(0, start);
     87 
     88     if (array) {
     89       push.apply(otherArgs, array);
     90     }
     91     if (start != lastIndex) {
     92       push.apply(otherArgs, args.slice(start + 1));
     93     }
     94     return func.apply(this, otherArgs);
     95   };
     96 }
     97 
     98 /**
     99  * Creates a function that wraps `func` and uses `cloner` to clone the first
    100  * argument it receives.
    101  *
    102  * @private
    103  * @param {Function} func The function to wrap.
    104  * @param {Function} cloner The function to clone arguments.
    105  * @returns {Function} Returns the new immutable function.
    106  */
    107 function wrapImmutable(func, cloner) {
    108   return function() {
    109     var length = arguments.length;
    110     if (!length) {
    111       return;
    112     }
    113     var args = Array(length);
    114     while (length--) {
    115       args[length] = arguments[length];
    116     }
    117     var result = args[0] = cloner.apply(undefined, args);
    118     func.apply(undefined, args);
    119     return result;
    120   };
    121 }
    122 
    123 /**
    124  * The base implementation of `convert` which accepts a `util` object of methods
    125  * required to perform conversions.
    126  *
    127  * @param {Object} util The util object.
    128  * @param {string} name The name of the function to convert.
    129  * @param {Function} func The function to convert.
    130  * @param {Object} [options] The options object.
    131  * @param {boolean} [options.cap=true] Specify capping iteratee arguments.
    132  * @param {boolean} [options.curry=true] Specify currying.
    133  * @param {boolean} [options.fixed=true] Specify fixed arity.
    134  * @param {boolean} [options.immutable=true] Specify immutable operations.
    135  * @param {boolean} [options.rearg=true] Specify rearranging arguments.
    136  * @returns {Function|Object} Returns the converted function or object.
    137  */
    138 function baseConvert(util, name, func, options) {
    139   var isLib = typeof name == 'function',
    140       isObj = name === Object(name);
    141 
    142   if (isObj) {
    143     options = func;
    144     func = name;
    145     name = undefined;
    146   }
    147   if (func == null) {
    148     throw new TypeError;
    149   }
    150   options || (options = {});
    151 
    152   var config = {
    153     'cap': 'cap' in options ? options.cap : true,
    154     'curry': 'curry' in options ? options.curry : true,
    155     'fixed': 'fixed' in options ? options.fixed : true,
    156     'immutable': 'immutable' in options ? options.immutable : true,
    157     'rearg': 'rearg' in options ? options.rearg : true
    158   };
    159 
    160   var defaultHolder = isLib ? func : fallbackHolder,
    161       forceCurry = ('curry' in options) && options.curry,
    162       forceFixed = ('fixed' in options) && options.fixed,
    163       forceRearg = ('rearg' in options) && options.rearg,
    164       pristine = isLib ? func.runInContext() : undefined;
    165 
    166   var helpers = isLib ? func : {
    167     'ary': util.ary,
    168     'assign': util.assign,
    169     'clone': util.clone,
    170     'curry': util.curry,
    171     'forEach': util.forEach,
    172     'isArray': util.isArray,
    173     'isError': util.isError,
    174     'isFunction': util.isFunction,
    175     'isWeakMap': util.isWeakMap,
    176     'iteratee': util.iteratee,
    177     'keys': util.keys,
    178     'rearg': util.rearg,
    179     'toInteger': util.toInteger,
    180     'toPath': util.toPath
    181   };
    182 
    183   var ary = helpers.ary,
    184       assign = helpers.assign,
    185       clone = helpers.clone,
    186       curry = helpers.curry,
    187       each = helpers.forEach,
    188       isArray = helpers.isArray,
    189       isError = helpers.isError,
    190       isFunction = helpers.isFunction,
    191       isWeakMap = helpers.isWeakMap,
    192       keys = helpers.keys,
    193       rearg = helpers.rearg,
    194       toInteger = helpers.toInteger,
    195       toPath = helpers.toPath;
    196 
    197   var aryMethodKeys = keys(mapping.aryMethod);
    198 
    199   var wrappers = {
    200     'castArray': function(castArray) {
    201       return function() {
    202         var value = arguments[0];
    203         return isArray(value)
    204           ? castArray(cloneArray(value))
    205           : castArray.apply(undefined, arguments);
    206       };
    207     },
    208     'iteratee': function(iteratee) {
    209       return function() {
    210         var func = arguments[0],
    211             arity = arguments[1],
    212             result = iteratee(func, arity),
    213             length = result.length;
    214 
    215         if (config.cap && typeof arity == 'number') {
    216           arity = arity > 2 ? (arity - 2) : 1;
    217           return (length && length <= arity) ? result : baseAry(result, arity);
    218         }
    219         return result;
    220       };
    221     },
    222     'mixin': function(mixin) {
    223       return function(source) {
    224         var func = this;
    225         if (!isFunction(func)) {
    226           return mixin(func, Object(source));
    227         }
    228         var pairs = [];
    229         each(keys(source), function(key) {
    230           if (isFunction(source[key])) {
    231             pairs.push([key, func.prototype[key]]);
    232           }
    233         });
    234 
    235         mixin(func, Object(source));
    236 
    237         each(pairs, function(pair) {
    238           var value = pair[1];
    239           if (isFunction(value)) {
    240             func.prototype[pair[0]] = value;
    241           } else {
    242             delete func.prototype[pair[0]];
    243           }
    244         });
    245         return func;
    246       };
    247     },
    248     'nthArg': function(nthArg) {
    249       return function(n) {
    250         var arity = n < 0 ? 1 : (toInteger(n) + 1);
    251         return curry(nthArg(n), arity);
    252       };
    253     },
    254     'rearg': function(rearg) {
    255       return function(func, indexes) {
    256         var arity = indexes ? indexes.length : 0;
    257         return curry(rearg(func, indexes), arity);
    258       };
    259     },
    260     'runInContext': function(runInContext) {
    261       return function(context) {
    262         return baseConvert(util, runInContext(context), options);
    263       };
    264     }
    265   };
    266 
    267   /*--------------------------------------------------------------------------*/
    268 
    269   /**
    270    * Casts `func` to a function with an arity capped iteratee if needed.
    271    *
    272    * @private
    273    * @param {string} name The name of the function to inspect.
    274    * @param {Function} func The function to inspect.
    275    * @returns {Function} Returns the cast function.
    276    */
    277   function castCap(name, func) {
    278     if (config.cap) {
    279       var indexes = mapping.iterateeRearg[name];
    280       if (indexes) {
    281         return iterateeRearg(func, indexes);
    282       }
    283       var n = !isLib && mapping.iterateeAry[name];
    284       if (n) {
    285         return iterateeAry(func, n);
    286       }
    287     }
    288     return func;
    289   }
    290 
    291   /**
    292    * Casts `func` to a curried function if needed.
    293    *
    294    * @private
    295    * @param {string} name The name of the function to inspect.
    296    * @param {Function} func The function to inspect.
    297    * @param {number} n The arity of `func`.
    298    * @returns {Function} Returns the cast function.
    299    */
    300   function castCurry(name, func, n) {
    301     return (forceCurry || (config.curry && n > 1))
    302       ? curry(func, n)
    303       : func;
    304   }
    305 
    306   /**
    307    * Casts `func` to a fixed arity function if needed.
    308    *
    309    * @private
    310    * @param {string} name The name of the function to inspect.
    311    * @param {Function} func The function to inspect.
    312    * @param {number} n The arity cap.
    313    * @returns {Function} Returns the cast function.
    314    */
    315   function castFixed(name, func, n) {
    316     if (config.fixed && (forceFixed || !mapping.skipFixed[name])) {
    317       var data = mapping.methodSpread[name],
    318           start = data && data.start;
    319 
    320       return start  === undefined ? ary(func, n) : flatSpread(func, start);
    321     }
    322     return func;
    323   }
    324 
    325   /**
    326    * Casts `func` to an rearged function if needed.
    327    *
    328    * @private
    329    * @param {string} name The name of the function to inspect.
    330    * @param {Function} func The function to inspect.
    331    * @param {number} n The arity of `func`.
    332    * @returns {Function} Returns the cast function.
    333    */
    334   function castRearg(name, func, n) {
    335     return (config.rearg && n > 1 && (forceRearg || !mapping.skipRearg[name]))
    336       ? rearg(func, mapping.methodRearg[name] || mapping.aryRearg[n])
    337       : func;
    338   }
    339 
    340   /**
    341    * Creates a clone of `object` by `path`.
    342    *
    343    * @private
    344    * @param {Object} object The object to clone.
    345    * @param {Array|string} path The path to clone by.
    346    * @returns {Object} Returns the cloned object.
    347    */
    348   function cloneByPath(object, path) {
    349     path = toPath(path);
    350 
    351     var index = -1,
    352         length = path.length,
    353         lastIndex = length - 1,
    354         result = clone(Object(object)),
    355         nested = result;
    356 
    357     while (nested != null && ++index < length) {
    358       var key = path[index],
    359           value = nested[key];
    360 
    361       if (value != null &&
    362           !(isFunction(value) || isError(value) || isWeakMap(value))) {
    363         nested[key] = clone(index == lastIndex ? value : Object(value));
    364       }
    365       nested = nested[key];
    366     }
    367     return result;
    368   }
    369 
    370   /**
    371    * Converts `lodash` to an immutable auto-curried iteratee-first data-last
    372    * version with conversion `options` applied.
    373    *
    374    * @param {Object} [options] The options object. See `baseConvert` for more details.
    375    * @returns {Function} Returns the converted `lodash`.
    376    */
    377   function convertLib(options) {
    378     return _.runInContext.convert(options)(undefined);
    379   }
    380 
    381   /**
    382    * Create a converter function for `func` of `name`.
    383    *
    384    * @param {string} name The name of the function to convert.
    385    * @param {Function} func The function to convert.
    386    * @returns {Function} Returns the new converter function.
    387    */
    388   function createConverter(name, func) {
    389     var realName = mapping.aliasToReal[name] || name,
    390         methodName = mapping.remap[realName] || realName,
    391         oldOptions = options;
    392 
    393     return function(options) {
    394       var newUtil = isLib ? pristine : helpers,
    395           newFunc = isLib ? pristine[methodName] : func,
    396           newOptions = assign(assign({}, oldOptions), options);
    397 
    398       return baseConvert(newUtil, realName, newFunc, newOptions);
    399     };
    400   }
    401 
    402   /**
    403    * Creates a function that wraps `func` to invoke its iteratee, with up to `n`
    404    * arguments, ignoring any additional arguments.
    405    *
    406    * @private
    407    * @param {Function} func The function to cap iteratee arguments for.
    408    * @param {number} n The arity cap.
    409    * @returns {Function} Returns the new function.
    410    */
    411   function iterateeAry(func, n) {
    412     return overArg(func, function(func) {
    413       return typeof func == 'function' ? baseAry(func, n) : func;
    414     });
    415   }
    416 
    417   /**
    418    * Creates a function that wraps `func` to invoke its iteratee with arguments
    419    * arranged according to the specified `indexes` where the argument value at
    420    * the first index is provided as the first argument, the argument value at
    421    * the second index is provided as the second argument, and so on.
    422    *
    423    * @private
    424    * @param {Function} func The function to rearrange iteratee arguments for.
    425    * @param {number[]} indexes The arranged argument indexes.
    426    * @returns {Function} Returns the new function.
    427    */
    428   function iterateeRearg(func, indexes) {
    429     return overArg(func, function(func) {
    430       var n = indexes.length;
    431       return baseArity(rearg(baseAry(func, n), indexes), n);
    432     });
    433   }
    434 
    435   /**
    436    * Creates a function that invokes `func` with its first argument transformed.
    437    *
    438    * @private
    439    * @param {Function} func The function to wrap.
    440    * @param {Function} transform The argument transform.
    441    * @returns {Function} Returns the new function.
    442    */
    443   function overArg(func, transform) {
    444     return function() {
    445       var length = arguments.length;
    446       if (!length) {
    447         return func();
    448       }
    449       var args = Array(length);
    450       while (length--) {
    451         args[length] = arguments[length];
    452       }
    453       var index = config.rearg ? 0 : (length - 1);
    454       args[index] = transform(args[index]);
    455       return func.apply(undefined, args);
    456     };
    457   }
    458 
    459   /**
    460    * Creates a function that wraps `func` and applys the conversions
    461    * rules by `name`.
    462    *
    463    * @private
    464    * @param {string} name The name of the function to wrap.
    465    * @param {Function} func The function to wrap.
    466    * @returns {Function} Returns the converted function.
    467    */
    468   function wrap(name, func, placeholder) {
    469     var result,
    470         realName = mapping.aliasToReal[name] || name,
    471         wrapped = func,
    472         wrapper = wrappers[realName];
    473 
    474     if (wrapper) {
    475       wrapped = wrapper(func);
    476     }
    477     else if (config.immutable) {
    478       if (mapping.mutate.array[realName]) {
    479         wrapped = wrapImmutable(func, cloneArray);
    480       }
    481       else if (mapping.mutate.object[realName]) {
    482         wrapped = wrapImmutable(func, createCloner(func));
    483       }
    484       else if (mapping.mutate.set[realName]) {
    485         wrapped = wrapImmutable(func, cloneByPath);
    486       }
    487     }
    488     each(aryMethodKeys, function(aryKey) {
    489       each(mapping.aryMethod[aryKey], function(otherName) {
    490         if (realName == otherName) {
    491           var data = mapping.methodSpread[realName],
    492               afterRearg = data && data.afterRearg;
    493 
    494           result = afterRearg
    495             ? castFixed(realName, castRearg(realName, wrapped, aryKey), aryKey)
    496             : castRearg(realName, castFixed(realName, wrapped, aryKey), aryKey);
    497 
    498           result = castCap(realName, result);
    499           result = castCurry(realName, result, aryKey);
    500           return false;
    501         }
    502       });
    503       return !result;
    504     });
    505 
    506     result || (result = wrapped);
    507     if (result == func) {
    508       result = forceCurry ? curry(result, 1) : function() {
    509         return func.apply(this, arguments);
    510       };
    511     }
    512     result.convert = createConverter(realName, func);
    513     result.placeholder = func.placeholder = placeholder;
    514 
    515     return result;
    516   }
    517 
    518   /*--------------------------------------------------------------------------*/
    519 
    520   if (!isObj) {
    521     return wrap(name, func, defaultHolder);
    522   }
    523   var _ = func;
    524 
    525   // Convert methods by ary cap.
    526   var pairs = [];
    527   each(aryMethodKeys, function(aryKey) {
    528     each(mapping.aryMethod[aryKey], function(key) {
    529       var func = _[mapping.remap[key] || key];
    530       if (func) {
    531         pairs.push([key, wrap(key, func, _)]);
    532       }
    533     });
    534   });
    535 
    536   // Convert remaining methods.
    537   each(keys(_), function(key) {
    538     var func = _[key];
    539     if (typeof func == 'function') {
    540       var length = pairs.length;
    541       while (length--) {
    542         if (pairs[length][0] == key) {
    543           return;
    544         }
    545       }
    546       func.convert = createConverter(key, func);
    547       pairs.push([key, func]);
    548     }
    549   });
    550 
    551   // Assign to `_` leaving `_.prototype` unchanged to allow chaining.
    552   each(pairs, function(pair) {
    553     _[pair[0]] = pair[1];
    554   });
    555 
    556   _.convert = convertLib;
    557   _.placeholder = _;
    558 
    559   // Assign aliases.
    560   each(keys(_), function(key) {
    561     each(mapping.realToAlias[key] || [], function(alias) {
    562       _[alias] = _[key];
    563     });
    564   });
    565 
    566   return _;
    567 }
    568 
    569 module.exports = baseConvert;