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