You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							315 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
	
	
							315 lines
						
					
					
						
							12 KiB
						
					
					
				| "use strict";
 | |
| module.exports = function(Promise, INTERNAL) {
 | |
| var THIS = {};
 | |
| var util = require("./util");
 | |
| var nodebackForPromise = require("./nodeback");
 | |
| var withAppended = util.withAppended;
 | |
| var maybeWrapAsError = util.maybeWrapAsError;
 | |
| var canEvaluate = util.canEvaluate;
 | |
| var TypeError = require("./errors").TypeError;
 | |
| var defaultSuffix = "Async";
 | |
| var defaultPromisified = {__isPromisified__: true};
 | |
| var noCopyProps = [
 | |
|     "arity",    "length",
 | |
|     "name",
 | |
|     "arguments",
 | |
|     "caller",
 | |
|     "callee",
 | |
|     "prototype",
 | |
|     "__isPromisified__"
 | |
| ];
 | |
| var noCopyPropsPattern = new RegExp("^(?:" + noCopyProps.join("|") + ")$");
 | |
| 
 | |
| var defaultFilter = function(name) {
 | |
|     return util.isIdentifier(name) &&
 | |
|         name.charAt(0) !== "_" &&
 | |
|         name !== "constructor";
 | |
| };
 | |
| 
 | |
| function propsFilter(key) {
 | |
|     return !noCopyPropsPattern.test(key);
 | |
| }
 | |
| 
 | |
| function isPromisified(fn) {
 | |
|     try {
 | |
|         return fn.__isPromisified__ === true;
 | |
|     }
 | |
|     catch (e) {
 | |
|         return false;
 | |
|     }
 | |
| }
 | |
| 
 | |
| function hasPromisified(obj, key, suffix) {
 | |
|     var val = util.getDataPropertyOrDefault(obj, key + suffix,
 | |
|                                             defaultPromisified);
 | |
|     return val ? isPromisified(val) : false;
 | |
| }
 | |
| function checkValid(ret, suffix, suffixRegexp) {
 | |
|     for (var i = 0; i < ret.length; i += 2) {
 | |
|         var key = ret[i];
 | |
|         if (suffixRegexp.test(key)) {
 | |
|             var keyWithoutAsyncSuffix = key.replace(suffixRegexp, "");
 | |
|             for (var j = 0; j < ret.length; j += 2) {
 | |
|                 if (ret[j] === keyWithoutAsyncSuffix) {
 | |
|                     throw new TypeError("Cannot promisify an API that has normal methods with '%s'-suffix\u000a\u000a    See http://goo.gl/MqrFmX\u000a"
 | |
|                         .replace("%s", suffix));
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| function promisifiableMethods(obj, suffix, suffixRegexp, filter) {
 | |
|     var keys = util.inheritedDataKeys(obj);
 | |
|     var ret = [];
 | |
|     for (var i = 0; i < keys.length; ++i) {
 | |
|         var key = keys[i];
 | |
|         var value = obj[key];
 | |
|         var passesDefaultFilter = filter === defaultFilter
 | |
|             ? true : defaultFilter(key, value, obj);
 | |
|         if (typeof value === "function" &&
 | |
|             !isPromisified(value) &&
 | |
|             !hasPromisified(obj, key, suffix) &&
 | |
|             filter(key, value, obj, passesDefaultFilter)) {
 | |
|             ret.push(key, value);
 | |
|         }
 | |
|     }
 | |
|     checkValid(ret, suffix, suffixRegexp);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| var escapeIdentRegex = function(str) {
 | |
|     return str.replace(/([$])/, "\\$");
 | |
| };
 | |
| 
 | |
| var makeNodePromisifiedEval;
 | |
| if (!false) {
 | |
| var switchCaseArgumentOrder = function(likelyArgumentCount) {
 | |
|     var ret = [likelyArgumentCount];
 | |
|     var min = Math.max(0, likelyArgumentCount - 1 - 3);
 | |
|     for(var i = likelyArgumentCount - 1; i >= min; --i) {
 | |
|         ret.push(i);
 | |
|     }
 | |
|     for(var i = likelyArgumentCount + 1; i <= 3; ++i) {
 | |
|         ret.push(i);
 | |
|     }
 | |
|     return ret;
 | |
| };
 | |
| 
 | |
| var argumentSequence = function(argumentCount) {
 | |
|     return util.filledRange(argumentCount, "_arg", "");
 | |
| };
 | |
| 
 | |
| var parameterDeclaration = function(parameterCount) {
 | |
|     return util.filledRange(
 | |
|         Math.max(parameterCount, 3), "_arg", "");
 | |
| };
 | |
| 
 | |
| var parameterCount = function(fn) {
 | |
|     if (typeof fn.length === "number") {
 | |
|         return Math.max(Math.min(fn.length, 1023 + 1), 0);
 | |
|     }
 | |
|     return 0;
 | |
| };
 | |
| 
 | |
| makeNodePromisifiedEval =
 | |
| function(callback, receiver, originalName, fn, _, multiArgs) {
 | |
|     var newParameterCount = Math.max(0, parameterCount(fn) - 1);
 | |
|     var argumentOrder = switchCaseArgumentOrder(newParameterCount);
 | |
|     var shouldProxyThis = typeof callback === "string" || receiver === THIS;
 | |
| 
 | |
|     function generateCallForArgumentCount(count) {
 | |
|         var args = argumentSequence(count).join(", ");
 | |
|         var comma = count > 0 ? ", " : "";
 | |
|         var ret;
 | |
|         if (shouldProxyThis) {
 | |
|             ret = "ret = callback.call(this, {{args}}, nodeback); break;\n";
 | |
|         } else {
 | |
|             ret = receiver === undefined
 | |
|                 ? "ret = callback({{args}}, nodeback); break;\n"
 | |
|                 : "ret = callback.call(receiver, {{args}}, nodeback); break;\n";
 | |
|         }
 | |
|         return ret.replace("{{args}}", args).replace(", ", comma);
 | |
|     }
 | |
| 
 | |
|     function generateArgumentSwitchCase() {
 | |
|         var ret = "";
 | |
|         for (var i = 0; i < argumentOrder.length; ++i) {
 | |
|             ret += "case " + argumentOrder[i] +":" +
 | |
|                 generateCallForArgumentCount(argumentOrder[i]);
 | |
|         }
 | |
| 
 | |
|         ret += "                                                             \n\
 | |
|         default:                                                             \n\
 | |
|             var args = new Array(len + 1);                                   \n\
 | |
|             var i = 0;                                                       \n\
 | |
|             for (var i = 0; i < len; ++i) {                                  \n\
 | |
|                args[i] = arguments[i];                                       \n\
 | |
|             }                                                                \n\
 | |
|             args[i] = nodeback;                                              \n\
 | |
|             [CodeForCall]                                                    \n\
 | |
|             break;                                                           \n\
 | |
|         ".replace("[CodeForCall]", (shouldProxyThis
 | |
|                                 ? "ret = callback.apply(this, args);\n"
 | |
|                                 : "ret = callback.apply(receiver, args);\n"));
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     var getFunctionCode = typeof callback === "string"
 | |
|                                 ? ("this != null ? this['"+callback+"'] : fn")
 | |
|                                 : "fn";
 | |
|     var body = "'use strict';                                                \n\
 | |
|         var ret = function (Parameters) {                                    \n\
 | |
|             'use strict';                                                    \n\
 | |
|             var len = arguments.length;                                      \n\
 | |
|             var promise = new Promise(INTERNAL);                             \n\
 | |
|             promise._captureStackTrace();                                    \n\
 | |
|             var nodeback = nodebackForPromise(promise, " + multiArgs + ");   \n\
 | |
|             var ret;                                                         \n\
 | |
|             var callback = tryCatch([GetFunctionCode]);                      \n\
 | |
|             switch(len) {                                                    \n\
 | |
|                 [CodeForSwitchCase]                                          \n\
 | |
|             }                                                                \n\
 | |
|             if (ret === errorObj) {                                          \n\
 | |
|                 promise._rejectCallback(maybeWrapAsError(ret.e), true, true);\n\
 | |
|             }                                                                \n\
 | |
|             if (!promise._isFateSealed()) promise._setAsyncGuaranteed();     \n\
 | |
|             return promise;                                                  \n\
 | |
|         };                                                                   \n\
 | |
|         notEnumerableProp(ret, '__isPromisified__', true);                   \n\
 | |
|         return ret;                                                          \n\
 | |
|     ".replace("[CodeForSwitchCase]", generateArgumentSwitchCase())
 | |
|         .replace("[GetFunctionCode]", getFunctionCode);
 | |
|     body = body.replace("Parameters", parameterDeclaration(newParameterCount));
 | |
|     return new Function("Promise",
 | |
|                         "fn",
 | |
|                         "receiver",
 | |
|                         "withAppended",
 | |
|                         "maybeWrapAsError",
 | |
|                         "nodebackForPromise",
 | |
|                         "tryCatch",
 | |
|                         "errorObj",
 | |
|                         "notEnumerableProp",
 | |
|                         "INTERNAL",
 | |
|                         body)(
 | |
|                     Promise,
 | |
|                     fn,
 | |
|                     receiver,
 | |
|                     withAppended,
 | |
|                     maybeWrapAsError,
 | |
|                     nodebackForPromise,
 | |
|                     util.tryCatch,
 | |
|                     util.errorObj,
 | |
|                     util.notEnumerableProp,
 | |
|                     INTERNAL);
 | |
| };
 | |
| }
 | |
| 
 | |
| function makeNodePromisifiedClosure(callback, receiver, _, fn, __, multiArgs) {
 | |
|     var defaultThis = (function() {return this;})();
 | |
|     var method = callback;
 | |
|     if (typeof method === "string") {
 | |
|         callback = fn;
 | |
|     }
 | |
|     function promisified() {
 | |
|         var _receiver = receiver;
 | |
|         if (receiver === THIS) _receiver = this;
 | |
|         var promise = new Promise(INTERNAL);
 | |
|         promise._captureStackTrace();
 | |
|         var cb = typeof method === "string" && this !== defaultThis
 | |
|             ? this[method] : callback;
 | |
|         var fn = nodebackForPromise(promise, multiArgs);
 | |
|         try {
 | |
|             cb.apply(_receiver, withAppended(arguments, fn));
 | |
|         } catch(e) {
 | |
|             promise._rejectCallback(maybeWrapAsError(e), true, true);
 | |
|         }
 | |
|         if (!promise._isFateSealed()) promise._setAsyncGuaranteed();
 | |
|         return promise;
 | |
|     }
 | |
|     util.notEnumerableProp(promisified, "__isPromisified__", true);
 | |
|     return promisified;
 | |
| }
 | |
| 
 | |
| var makeNodePromisified = canEvaluate
 | |
|     ? makeNodePromisifiedEval
 | |
|     : makeNodePromisifiedClosure;
 | |
| 
 | |
| function promisifyAll(obj, suffix, filter, promisifier, multiArgs) {
 | |
|     var suffixRegexp = new RegExp(escapeIdentRegex(suffix) + "$");
 | |
|     var methods =
 | |
|         promisifiableMethods(obj, suffix, suffixRegexp, filter);
 | |
| 
 | |
|     for (var i = 0, len = methods.length; i < len; i+= 2) {
 | |
|         var key = methods[i];
 | |
|         var fn = methods[i+1];
 | |
|         var promisifiedKey = key + suffix;
 | |
|         if (promisifier === makeNodePromisified) {
 | |
|             obj[promisifiedKey] =
 | |
|                 makeNodePromisified(key, THIS, key, fn, suffix, multiArgs);
 | |
|         } else {
 | |
|             var promisified = promisifier(fn, function() {
 | |
|                 return makeNodePromisified(key, THIS, key,
 | |
|                                            fn, suffix, multiArgs);
 | |
|             });
 | |
|             util.notEnumerableProp(promisified, "__isPromisified__", true);
 | |
|             obj[promisifiedKey] = promisified;
 | |
|         }
 | |
|     }
 | |
|     util.toFastProperties(obj);
 | |
|     return obj;
 | |
| }
 | |
| 
 | |
| function promisify(callback, receiver, multiArgs) {
 | |
|     return makeNodePromisified(callback, receiver, undefined,
 | |
|                                 callback, null, multiArgs);
 | |
| }
 | |
| 
 | |
| Promise.promisify = function (fn, options) {
 | |
|     if (typeof fn !== "function") {
 | |
|         throw new TypeError("expecting a function but got " + util.classString(fn));
 | |
|     }
 | |
|     if (isPromisified(fn)) {
 | |
|         return fn;
 | |
|     }
 | |
|     options = Object(options);
 | |
|     var receiver = options.context === undefined ? THIS : options.context;
 | |
|     var multiArgs = !!options.multiArgs;
 | |
|     var ret = promisify(fn, receiver, multiArgs);
 | |
|     util.copyDescriptors(fn, ret, propsFilter);
 | |
|     return ret;
 | |
| };
 | |
| 
 | |
| Promise.promisifyAll = function (target, options) {
 | |
|     if (typeof target !== "function" && typeof target !== "object") {
 | |
|         throw new TypeError("the target of promisifyAll must be an object or a function\u000a\u000a    See http://goo.gl/MqrFmX\u000a");
 | |
|     }
 | |
|     options = Object(options);
 | |
|     var multiArgs = !!options.multiArgs;
 | |
|     var suffix = options.suffix;
 | |
|     if (typeof suffix !== "string") suffix = defaultSuffix;
 | |
|     var filter = options.filter;
 | |
|     if (typeof filter !== "function") filter = defaultFilter;
 | |
|     var promisifier = options.promisifier;
 | |
|     if (typeof promisifier !== "function") promisifier = makeNodePromisified;
 | |
| 
 | |
|     if (!util.isIdentifier(suffix)) {
 | |
|         throw new RangeError("suffix must be a valid identifier\u000a\u000a    See http://goo.gl/MqrFmX\u000a");
 | |
|     }
 | |
| 
 | |
|     var keys = util.inheritedDataKeys(target);
 | |
|     for (var i = 0; i < keys.length; ++i) {
 | |
|         var value = target[keys[i]];
 | |
|         if (keys[i] !== "constructor" &&
 | |
|             util.isClass(value)) {
 | |
|             promisifyAll(value.prototype, suffix, filter, promisifier,
 | |
|                 multiArgs);
 | |
|             promisifyAll(value, suffix, filter, promisifier, multiArgs);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return promisifyAll(target, suffix, filter, promisifier, multiArgs);
 | |
| };
 | |
| };
 | |
| 
 |