twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

promisify.js (12182B)


      1 "use strict";
      2 module.exports = function(Promise, INTERNAL) {
      3 var THIS = {};
      4 var util = require("./util");
      5 var nodebackForPromise = require("./nodeback");
      6 var withAppended = util.withAppended;
      7 var maybeWrapAsError = util.maybeWrapAsError;
      8 var canEvaluate = util.canEvaluate;
      9 var TypeError = require("./errors").TypeError;
     10 var defaultSuffix = "Async";
     11 var defaultPromisified = {__isPromisified__: true};
     12 var noCopyProps = [
     13     "arity",    "length",
     14     "name",
     15     "arguments",
     16     "caller",
     17     "callee",
     18     "prototype",
     19     "__isPromisified__"
     20 ];
     21 var noCopyPropsPattern = new RegExp("^(?:" + noCopyProps.join("|") + ")$");
     22 
     23 var defaultFilter = function(name) {
     24     return util.isIdentifier(name) &&
     25         name.charAt(0) !== "_" &&
     26         name !== "constructor";
     27 };
     28 
     29 function propsFilter(key) {
     30     return !noCopyPropsPattern.test(key);
     31 }
     32 
     33 function isPromisified(fn) {
     34     try {
     35         return fn.__isPromisified__ === true;
     36     }
     37     catch (e) {
     38         return false;
     39     }
     40 }
     41 
     42 function hasPromisified(obj, key, suffix) {
     43     var val = util.getDataPropertyOrDefault(obj, key + suffix,
     44                                             defaultPromisified);
     45     return val ? isPromisified(val) : false;
     46 }
     47 function checkValid(ret, suffix, suffixRegexp) {
     48     for (var i = 0; i < ret.length; i += 2) {
     49         var key = ret[i];
     50         if (suffixRegexp.test(key)) {
     51             var keyWithoutAsyncSuffix = key.replace(suffixRegexp, "");
     52             for (var j = 0; j < ret.length; j += 2) {
     53                 if (ret[j] === keyWithoutAsyncSuffix) {
     54                     throw new TypeError("Cannot promisify an API that has normal methods with '%s'-suffix\u000a\u000a    See http://goo.gl/MqrFmX\u000a"
     55                         .replace("%s", suffix));
     56                 }
     57             }
     58         }
     59     }
     60 }
     61 
     62 function promisifiableMethods(obj, suffix, suffixRegexp, filter) {
     63     var keys = util.inheritedDataKeys(obj);
     64     var ret = [];
     65     for (var i = 0; i < keys.length; ++i) {
     66         var key = keys[i];
     67         var value = obj[key];
     68         var passesDefaultFilter = filter === defaultFilter
     69             ? true : defaultFilter(key, value, obj);
     70         if (typeof value === "function" &&
     71             !isPromisified(value) &&
     72             !hasPromisified(obj, key, suffix) &&
     73             filter(key, value, obj, passesDefaultFilter)) {
     74             ret.push(key, value);
     75         }
     76     }
     77     checkValid(ret, suffix, suffixRegexp);
     78     return ret;
     79 }
     80 
     81 var escapeIdentRegex = function(str) {
     82     return str.replace(/([$])/, "\\$");
     83 };
     84 
     85 var makeNodePromisifiedEval;
     86 if (!false) {
     87 var switchCaseArgumentOrder = function(likelyArgumentCount) {
     88     var ret = [likelyArgumentCount];
     89     var min = Math.max(0, likelyArgumentCount - 1 - 3);
     90     for(var i = likelyArgumentCount - 1; i >= min; --i) {
     91         ret.push(i);
     92     }
     93     for(var i = likelyArgumentCount + 1; i <= 3; ++i) {
     94         ret.push(i);
     95     }
     96     return ret;
     97 };
     98 
     99 var argumentSequence = function(argumentCount) {
    100     return util.filledRange(argumentCount, "_arg", "");
    101 };
    102 
    103 var parameterDeclaration = function(parameterCount) {
    104     return util.filledRange(
    105         Math.max(parameterCount, 3), "_arg", "");
    106 };
    107 
    108 var parameterCount = function(fn) {
    109     if (typeof fn.length === "number") {
    110         return Math.max(Math.min(fn.length, 1023 + 1), 0);
    111     }
    112     return 0;
    113 };
    114 
    115 makeNodePromisifiedEval =
    116 function(callback, receiver, originalName, fn, _, multiArgs) {
    117     var newParameterCount = Math.max(0, parameterCount(fn) - 1);
    118     var argumentOrder = switchCaseArgumentOrder(newParameterCount);
    119     var shouldProxyThis = typeof callback === "string" || receiver === THIS;
    120 
    121     function generateCallForArgumentCount(count) {
    122         var args = argumentSequence(count).join(", ");
    123         var comma = count > 0 ? ", " : "";
    124         var ret;
    125         if (shouldProxyThis) {
    126             ret = "ret = callback.call(this, {{args}}, nodeback); break;\n";
    127         } else {
    128             ret = receiver === undefined
    129                 ? "ret = callback({{args}}, nodeback); break;\n"
    130                 : "ret = callback.call(receiver, {{args}}, nodeback); break;\n";
    131         }
    132         return ret.replace("{{args}}", args).replace(", ", comma);
    133     }
    134 
    135     function generateArgumentSwitchCase() {
    136         var ret = "";
    137         for (var i = 0; i < argumentOrder.length; ++i) {
    138             ret += "case " + argumentOrder[i] +":" +
    139                 generateCallForArgumentCount(argumentOrder[i]);
    140         }
    141 
    142         ret += "                                                             \n\
    143         default:                                                             \n\
    144             var args = new Array(len + 1);                                   \n\
    145             var i = 0;                                                       \n\
    146             for (var i = 0; i < len; ++i) {                                  \n\
    147                args[i] = arguments[i];                                       \n\
    148             }                                                                \n\
    149             args[i] = nodeback;                                              \n\
    150             [CodeForCall]                                                    \n\
    151             break;                                                           \n\
    152         ".replace("[CodeForCall]", (shouldProxyThis
    153                                 ? "ret = callback.apply(this, args);\n"
    154                                 : "ret = callback.apply(receiver, args);\n"));
    155         return ret;
    156     }
    157 
    158     var getFunctionCode = typeof callback === "string"
    159                                 ? ("this != null ? this['"+callback+"'] : fn")
    160                                 : "fn";
    161     var body = "'use strict';                                                \n\
    162         var ret = function (Parameters) {                                    \n\
    163             'use strict';                                                    \n\
    164             var len = arguments.length;                                      \n\
    165             var promise = new Promise(INTERNAL);                             \n\
    166             promise._captureStackTrace();                                    \n\
    167             var nodeback = nodebackForPromise(promise, " + multiArgs + ");   \n\
    168             var ret;                                                         \n\
    169             var callback = tryCatch([GetFunctionCode]);                      \n\
    170             switch(len) {                                                    \n\
    171                 [CodeForSwitchCase]                                          \n\
    172             }                                                                \n\
    173             if (ret === errorObj) {                                          \n\
    174                 promise._rejectCallback(maybeWrapAsError(ret.e), true, true);\n\
    175             }                                                                \n\
    176             if (!promise._isFateSealed()) promise._setAsyncGuaranteed();     \n\
    177             return promise;                                                  \n\
    178         };                                                                   \n\
    179         notEnumerableProp(ret, '__isPromisified__', true);                   \n\
    180         return ret;                                                          \n\
    181     ".replace("[CodeForSwitchCase]", generateArgumentSwitchCase())
    182         .replace("[GetFunctionCode]", getFunctionCode);
    183     body = body.replace("Parameters", parameterDeclaration(newParameterCount));
    184     return new Function("Promise",
    185                         "fn",
    186                         "receiver",
    187                         "withAppended",
    188                         "maybeWrapAsError",
    189                         "nodebackForPromise",
    190                         "tryCatch",
    191                         "errorObj",
    192                         "notEnumerableProp",
    193                         "INTERNAL",
    194                         body)(
    195                     Promise,
    196                     fn,
    197                     receiver,
    198                     withAppended,
    199                     maybeWrapAsError,
    200                     nodebackForPromise,
    201                     util.tryCatch,
    202                     util.errorObj,
    203                     util.notEnumerableProp,
    204                     INTERNAL);
    205 };
    206 }
    207 
    208 function makeNodePromisifiedClosure(callback, receiver, _, fn, __, multiArgs) {
    209     var defaultThis = (function() {return this;})();
    210     var method = callback;
    211     if (typeof method === "string") {
    212         callback = fn;
    213     }
    214     function promisified() {
    215         var _receiver = receiver;
    216         if (receiver === THIS) _receiver = this;
    217         var promise = new Promise(INTERNAL);
    218         promise._captureStackTrace();
    219         var cb = typeof method === "string" && this !== defaultThis
    220             ? this[method] : callback;
    221         var fn = nodebackForPromise(promise, multiArgs);
    222         try {
    223             cb.apply(_receiver, withAppended(arguments, fn));
    224         } catch(e) {
    225             promise._rejectCallback(maybeWrapAsError(e), true, true);
    226         }
    227         if (!promise._isFateSealed()) promise._setAsyncGuaranteed();
    228         return promise;
    229     }
    230     util.notEnumerableProp(promisified, "__isPromisified__", true);
    231     return promisified;
    232 }
    233 
    234 var makeNodePromisified = canEvaluate
    235     ? makeNodePromisifiedEval
    236     : makeNodePromisifiedClosure;
    237 
    238 function promisifyAll(obj, suffix, filter, promisifier, multiArgs) {
    239     var suffixRegexp = new RegExp(escapeIdentRegex(suffix) + "$");
    240     var methods =
    241         promisifiableMethods(obj, suffix, suffixRegexp, filter);
    242 
    243     for (var i = 0, len = methods.length; i < len; i+= 2) {
    244         var key = methods[i];
    245         var fn = methods[i+1];
    246         var promisifiedKey = key + suffix;
    247         if (promisifier === makeNodePromisified) {
    248             obj[promisifiedKey] =
    249                 makeNodePromisified(key, THIS, key, fn, suffix, multiArgs);
    250         } else {
    251             var promisified = promisifier(fn, function() {
    252                 return makeNodePromisified(key, THIS, key,
    253                                            fn, suffix, multiArgs);
    254             });
    255             util.notEnumerableProp(promisified, "__isPromisified__", true);
    256             obj[promisifiedKey] = promisified;
    257         }
    258     }
    259     util.toFastProperties(obj);
    260     return obj;
    261 }
    262 
    263 function promisify(callback, receiver, multiArgs) {
    264     return makeNodePromisified(callback, receiver, undefined,
    265                                 callback, null, multiArgs);
    266 }
    267 
    268 Promise.promisify = function (fn, options) {
    269     if (typeof fn !== "function") {
    270         throw new TypeError("expecting a function but got " + util.classString(fn));
    271     }
    272     if (isPromisified(fn)) {
    273         return fn;
    274     }
    275     options = Object(options);
    276     var receiver = options.context === undefined ? THIS : options.context;
    277     var multiArgs = !!options.multiArgs;
    278     var ret = promisify(fn, receiver, multiArgs);
    279     util.copyDescriptors(fn, ret, propsFilter);
    280     return ret;
    281 };
    282 
    283 Promise.promisifyAll = function (target, options) {
    284     if (typeof target !== "function" && typeof target !== "object") {
    285         throw new TypeError("the target of promisifyAll must be an object or a function\u000a\u000a    See http://goo.gl/MqrFmX\u000a");
    286     }
    287     options = Object(options);
    288     var multiArgs = !!options.multiArgs;
    289     var suffix = options.suffix;
    290     if (typeof suffix !== "string") suffix = defaultSuffix;
    291     var filter = options.filter;
    292     if (typeof filter !== "function") filter = defaultFilter;
    293     var promisifier = options.promisifier;
    294     if (typeof promisifier !== "function") promisifier = makeNodePromisified;
    295 
    296     if (!util.isIdentifier(suffix)) {
    297         throw new RangeError("suffix must be a valid identifier\u000a\u000a    See http://goo.gl/MqrFmX\u000a");
    298     }
    299 
    300     var keys = util.inheritedDataKeys(target);
    301     for (var i = 0; i < keys.length; ++i) {
    302         var value = target[keys[i]];
    303         if (keys[i] !== "constructor" &&
    304             util.isClass(value)) {
    305             promisifyAll(value.prototype, suffix, filter, promisifier,
    306                 multiArgs);
    307             promisifyAll(value, suffix, filter, promisifier, multiArgs);
    308         }
    309     }
    310 
    311     return promisifyAll(target, suffix, filter, promisifier, multiArgs);
    312 };
    313 };
    314