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
			| 
											3 years ago
										 | "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); | ||
|  | }; | ||
|  | }; | ||
|  | 
 |