"use strict"; module.exports = function(Promise, INTERNAL) { var THIS = {}; var util = require("./util.js"); var nodebackForPromise = require("./promise_resolver.js") ._nodebackForPromise; var withAppended = util.withAppended; var maybeWrapAsError = util.maybeWrapAsError; var canEvaluate = util.canEvaluate; var TypeError = require("./errors").TypeError; var defaultSuffix = "Async"; var defaultFilter = function(name, func) { return util.isIdentifier(name) && name.charAt(0) !== "_" && !util.isClass(func); }; var defaultPromisified = {__isPromisified__: true}; 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/iWrZbw\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 - 5); for(var i = likelyArgumentCount - 1; i >= min; --i) { ret.push(i); } for(var i = likelyArgumentCount + 1; i <= 5; ++i) { ret.push(i); } return ret; }; var argumentSequence = function(argumentCount) { return util.filledRange(argumentCount, "arguments[", "]"); }; var parameterDeclaration = function(parameterCount) { return util.filledRange(parameterCount, "_arg", ""); }; var parameterCount = function(fn) { if (typeof fn.length === "number") { return Math.max(Math.min(fn.length, 1023 + 1), 0); } return 0; }; var generatePropertyAccess = function(key) { if (util.isIdentifier(key)) { return "." + key; } else return "['" + key.replace(/(['\\])/g, "\\$1") + "']"; }; makeNodePromisifiedEval = function(callback, receiver, originalName, fn, suffix) { var newParameterCount = Math.max(0, parameterCount(fn) - 1); var argumentOrder = switchCaseArgumentOrder(newParameterCount); var callbackName = (typeof originalName === "string" && util.isIdentifier(originalName) ? originalName + suffix : "promisified"); function generateCallForArgumentCount(count) { var args = argumentSequence(count).join(", "); var comma = count > 0 ? ", " : ""; var ret; if (typeof callback === "string") { ret = " \n\ this.method({{args}}, fn); \n\ break; \n\ ".replace(".method", generatePropertyAccess(callback)); } else if (receiver === THIS) { ret = " \n\ callback.call(this, {{args}}, fn); \n\ break; \n\ "; } else if (receiver !== undefined) { ret = " \n\ callback.call(receiver, {{args}}, fn); \n\ break; \n\ "; } else { ret = " \n\ callback({{args}}, fn); \n\ 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]); } var codeForCall; if (typeof callback === "string") { codeForCall = " \n\ this.property.apply(this, args); \n\ " .replace(".property", generatePropertyAccess(callback)); } else if (receiver === THIS) { codeForCall = " \n\ callback.apply(this, args); \n\ "; } else { codeForCall = " \n\ callback.apply(receiver, args); \n\ "; } 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] = fn; \n\ [CodeForCall] \n\ break; \n\ ".replace("[CodeForCall]", codeForCall); return ret; } return new Function("Promise", "callback", "receiver", "withAppended", "maybeWrapAsError", "nodebackForPromise", "INTERNAL"," \n\ var ret = function (Parameters) { \n\ 'use strict'; \n\ var len = arguments.length; \n\ var promise = new Promise(INTERNAL); \n\ promise._captureStackTrace(); \n\ promise._setIsSpreadable(); \n\ var fn = nodebackForPromise(promise); \n\ try { \n\ switch(len) { \n\ [CodeForSwitchCase] \n\ } \n\ } catch (e) { \n\ var wrapped = maybeWrapAsError(e); \n\ promise._attachExtraTrace(wrapped); \n\ promise._reject(wrapped); \n\ } \n\ return promise; \n\ }; \n\ ret.__isPromisified__ = true; \n\ return ret; \n\ " .replace("FunctionName", callbackName) .replace("Parameters", parameterDeclaration(newParameterCount)) .replace("[CodeForSwitchCase]", generateArgumentSwitchCase()))( Promise, callback, receiver, withAppended, maybeWrapAsError, nodebackForPromise, INTERNAL ); }; } function makeNodePromisifiedClosure(callback, receiver) { function promisified() { var _receiver = receiver; if (receiver === THIS) _receiver = this; if (typeof callback === "string") { callback = _receiver[callback]; } var promise = new Promise(INTERNAL); promise._captureStackTrace(); promise._setIsSpreadable(); var fn = nodebackForPromise(promise); try { callback.apply(_receiver, withAppended(arguments, fn)); } catch(e) { var wrapped = maybeWrapAsError(e); promise._attachExtraTrace(wrapped); promise._reject(wrapped); } return promise; } promisified.__isPromisified__ = true; return promisified; } var makeNodePromisified = canEvaluate ? makeNodePromisifiedEval : makeNodePromisifiedClosure; function promisifyAll(obj, suffix, filter, promisifier) { 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; obj[promisifiedKey] = promisifier === makeNodePromisified ? makeNodePromisified(key, THIS, key, fn, suffix) : promisifier(fn, function() { return makeNodePromisified(key, THIS, key, fn, suffix); }); } util.toFastProperties(obj); return obj; } function promisify(callback, receiver) { return makeNodePromisified(callback, receiver, undefined, callback); } Promise.promisify = function (fn, receiver) { if (typeof fn !== "function") { throw new TypeError("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a"); } if (isPromisified(fn)) { return fn; } return promisify(fn, arguments.length < 2 ? THIS : receiver); }; 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/9ITlV0\u000a"); } options = Object(options); 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/8FZo5V\u000a"); } var keys = util.inheritedDataKeys(target, {includeHidden: true}); 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); promisifyAll(value, suffix, filter, promisifier); } } return promisifyAll(target, suffix, filter, promisifier); }; };