2 module.exports = function(Promise, INTERNAL) {
4 var util = require("./util.js");
5 var nodebackForPromise = require("./promise_resolver.js")
7 var withAppended = util.withAppended;
8 var maybeWrapAsError = util.maybeWrapAsError;
9 var canEvaluate = util.canEvaluate;
10 var TypeError = require("./errors").TypeError;
11 var defaultSuffix = "Async";
12 var defaultPromisified = {__isPromisified__: true};
22 var noCopyPropsPattern = new RegExp("^(?:" + noCopyProps.join("|") + ")$");
24 var defaultFilter = function(name) {
25 return util.isIdentifier(name) &&
26 name.charAt(0) !== "_" &&
27 name !== "constructor";
30 function propsFilter(key) {
31 return !noCopyPropsPattern.test(key);
34 function isPromisified(fn) {
36 return fn.__isPromisified__ === true;
43 function hasPromisified(obj, key, suffix) {
44 var val = util.getDataPropertyOrDefault(obj, key + suffix,
46 return val ? isPromisified(val) : false;
48 function checkValid(ret, suffix, suffixRegexp) {
49 for (var i = 0; i < ret.length; i += 2) {
51 if (suffixRegexp.test(key)) {
52 var keyWithoutAsyncSuffix = key.replace(suffixRegexp, "");
53 for (var j = 0; j < ret.length; j += 2) {
54 if (ret[j] === keyWithoutAsyncSuffix) {
55 throw new TypeError("Cannot promisify an API that has normal methods with '%s'-suffix\u000a\u000a See http://goo.gl/iWrZbw\u000a"
56 .replace("%s", suffix));
63 function promisifiableMethods(obj, suffix, suffixRegexp, filter) {
64 var keys = util.inheritedDataKeys(obj);
66 for (var i = 0; i < keys.length; ++i) {
69 var passesDefaultFilter = filter === defaultFilter
70 ? true : defaultFilter(key, value, obj);
71 if (typeof value === "function" &&
72 !isPromisified(value) &&
73 !hasPromisified(obj, key, suffix) &&
74 filter(key, value, obj, passesDefaultFilter)) {
78 checkValid(ret, suffix, suffixRegexp);
82 var escapeIdentRegex = function(str) {
83 return str.replace(/([$])/, "\\$");
86 var makeNodePromisifiedEval;
88 var switchCaseArgumentOrder = function(likelyArgumentCount) {
89 var ret = [likelyArgumentCount];
90 var min = Math.max(0, likelyArgumentCount - 1 - 3);
91 for(var i = likelyArgumentCount - 1; i >= min; --i) {
94 for(var i = likelyArgumentCount + 1; i <= 3; ++i) {
100 var argumentSequence = function(argumentCount) {
101 return util.filledRange(argumentCount, "_arg", "");
104 var parameterDeclaration = function(parameterCount) {
105 return util.filledRange(
106 Math.max(parameterCount, 3), "_arg", "");
109 var parameterCount = function(fn) {
110 if (typeof fn.length === "number") {
111 return Math.max(Math.min(fn.length, 1023 + 1), 0);
116 makeNodePromisifiedEval =
117 function(callback, receiver, originalName, fn) {
118 var newParameterCount = Math.max(0, parameterCount(fn) - 1);
119 var argumentOrder = switchCaseArgumentOrder(newParameterCount);
120 var shouldProxyThis = typeof callback === "string" || receiver === THIS;
122 function generateCallForArgumentCount(count) {
123 var args = argumentSequence(count).join(", ");
124 var comma = count > 0 ? ", " : "";
126 if (shouldProxyThis) {
127 ret = "ret = callback.call(this, {{args}}, nodeback); break;\n";
129 ret = receiver === undefined
130 ? "ret = callback({{args}}, nodeback); break;\n"
131 : "ret = callback.call(receiver, {{args}}, nodeback); break;\n";
133 return ret.replace("{{args}}", args).replace(", ", comma);
136 function generateArgumentSwitchCase() {
138 for (var i = 0; i < argumentOrder.length; ++i) {
139 ret += "case " + argumentOrder[i] +":" +
140 generateCallForArgumentCount(argumentOrder[i]);
145 var args = new Array(len + 1); \n\
147 for (var i = 0; i < len; ++i) { \n\
148 args[i] = arguments[i]; \n\
150 args[i] = nodeback; \n\
153 ".replace("[CodeForCall]", (shouldProxyThis
154 ? "ret = callback.apply(this, args);\n"
155 : "ret = callback.apply(receiver, args);\n"));
159 var getFunctionCode = typeof callback === "string"
160 ? ("this != null ? this['"+callback+"'] : fn")
163 return new Function("Promise",
168 "nodebackForPromise",
172 "INTERNAL","'use strict'; \n\
173 var ret = function (Parameters) { \n\
175 var len = arguments.length; \n\
176 var promise = new Promise(INTERNAL); \n\
177 promise._captureStackTrace(); \n\
178 var nodeback = nodebackForPromise(promise); \n\
180 var callback = tryCatch([GetFunctionCode]); \n\
182 [CodeForSwitchCase] \n\
184 if (ret === errorObj) { \n\
185 promise._rejectCallback(maybeWrapAsError(ret.e), true, true);\n\
189 notEnumerableProp(ret, '__isPromisified__', true); \n\
192 .replace("Parameters", parameterDeclaration(newParameterCount))
193 .replace("[CodeForSwitchCase]", generateArgumentSwitchCase())
194 .replace("[GetFunctionCode]", getFunctionCode))(
203 util.notEnumerableProp,
209 function makeNodePromisifiedClosure(callback, receiver, _, fn) {
210 var defaultThis = (function() {return this;})();
211 var method = callback;
212 if (typeof method === "string") {
215 function promisified() {
216 var _receiver = receiver;
217 if (receiver === THIS) _receiver = this;
218 var promise = new Promise(INTERNAL);
219 promise._captureStackTrace();
220 var cb = typeof method === "string" && this !== defaultThis
221 ? this[method] : callback;
222 var fn = nodebackForPromise(promise);
224 cb.apply(_receiver, withAppended(arguments, fn));
226 promise._rejectCallback(maybeWrapAsError(e), true, true);
230 util.notEnumerableProp(promisified, "__isPromisified__", true);
234 var makeNodePromisified = canEvaluate
235 ? makeNodePromisifiedEval
236 : makeNodePromisifiedClosure;
238 function promisifyAll(obj, suffix, filter, promisifier) {
239 var suffixRegexp = new RegExp(escapeIdentRegex(suffix) + "$");
241 promisifiableMethods(obj, suffix, suffixRegexp, filter);
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);
251 var promisified = promisifier(fn, function() {
252 return makeNodePromisified(key, THIS, key, fn, suffix);
254 util.notEnumerableProp(promisified, "__isPromisified__", true);
255 obj[promisifiedKey] = promisified;
258 util.toFastProperties(obj);
262 function promisify(callback, receiver) {
263 return makeNodePromisified(callback, receiver, undefined, callback);
266 Promise.promisify = function (fn, receiver) {
267 if (typeof fn !== "function") {
268 throw new TypeError("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
270 if (isPromisified(fn)) {
273 var ret = promisify(fn, arguments.length < 2 ? THIS : receiver);
274 util.copyDescriptors(fn, ret, propsFilter);
278 Promise.promisifyAll = function (target, options) {
279 if (typeof target !== "function" && typeof target !== "object") {
280 throw new TypeError("the target of promisifyAll must be an object or a function\u000a\u000a See http://goo.gl/9ITlV0\u000a");
282 options = Object(options);
283 var suffix = options.suffix;
284 if (typeof suffix !== "string") suffix = defaultSuffix;
285 var filter = options.filter;
286 if (typeof filter !== "function") filter = defaultFilter;
287 var promisifier = options.promisifier;
288 if (typeof promisifier !== "function") promisifier = makeNodePromisified;
290 if (!util.isIdentifier(suffix)) {
291 throw new RangeError("suffix must be a valid identifier\u000a\u000a See http://goo.gl/8FZo5V\u000a");
294 var keys = util.inheritedDataKeys(target);
295 for (var i = 0; i < keys.length; ++i) {
296 var value = target[keys[i]];
297 if (keys[i] !== "constructor" &&
298 util.isClass(value)) {
299 promisifyAll(value.prototype, suffix, filter, promisifier);
300 promisifyAll(value, suffix, filter, promisifier);
304 return promisifyAll(target, suffix, filter, promisifier);