Version 1
[yaffs-website] / node_modules / bluebird / js / release / promisify.js
1 "use strict";
2 module.exports = function(Promise, INTERNAL) {
3 var THIS = {};
4 var util = require("./util");
5 var nodebackForPromise = require("./nodeback");
6 var withAppended = util.withAppended;
7 var maybeWrapAsError = util.maybeWrapAsError;
8 var canEvaluate = util.canEvaluate;
9 var TypeError = require("./errors").TypeError;
10 var defaultSuffix = "Async";
11 var defaultPromisified = {__isPromisified__: true};
12 var noCopyProps = [
13     "arity",    "length",
14     "name",
15     "arguments",
16     "caller",
17     "callee",
18     "prototype",
19     "__isPromisified__"
20 ];
21 var noCopyPropsPattern = new RegExp("^(?:" + noCopyProps.join("|") + ")$");
22
23 var defaultFilter = function(name) {
24     return util.isIdentifier(name) &&
25         name.charAt(0) !== "_" &&
26         name !== "constructor";
27 };
28
29 function propsFilter(key) {
30     return !noCopyPropsPattern.test(key);
31 }
32
33 function isPromisified(fn) {
34     try {
35         return fn.__isPromisified__ === true;
36     }
37     catch (e) {
38         return false;
39     }
40 }
41
42 function hasPromisified(obj, key, suffix) {
43     var val = util.getDataPropertyOrDefault(obj, key + suffix,
44                                             defaultPromisified);
45     return val ? isPromisified(val) : false;
46 }
47 function checkValid(ret, suffix, suffixRegexp) {
48     for (var i = 0; i < ret.length; i += 2) {
49         var key = ret[i];
50         if (suffixRegexp.test(key)) {
51             var keyWithoutAsyncSuffix = key.replace(suffixRegexp, "");
52             for (var j = 0; j < ret.length; j += 2) {
53                 if (ret[j] === keyWithoutAsyncSuffix) {
54                     throw new TypeError("Cannot promisify an API that has normal methods with '%s'-suffix\u000a\u000a    See http://goo.gl/MqrFmX\u000a"
55                         .replace("%s", suffix));
56                 }
57             }
58         }
59     }
60 }
61
62 function promisifiableMethods(obj, suffix, suffixRegexp, filter) {
63     var keys = util.inheritedDataKeys(obj);
64     var ret = [];
65     for (var i = 0; i < keys.length; ++i) {
66         var key = keys[i];
67         var value = obj[key];
68         var passesDefaultFilter = filter === defaultFilter
69             ? true : defaultFilter(key, value, obj);
70         if (typeof value === "function" &&
71             !isPromisified(value) &&
72             !hasPromisified(obj, key, suffix) &&
73             filter(key, value, obj, passesDefaultFilter)) {
74             ret.push(key, value);
75         }
76     }
77     checkValid(ret, suffix, suffixRegexp);
78     return ret;
79 }
80
81 var escapeIdentRegex = function(str) {
82     return str.replace(/([$])/, "\\$");
83 };
84
85 var makeNodePromisifiedEval;
86 if (!false) {
87 var switchCaseArgumentOrder = function(likelyArgumentCount) {
88     var ret = [likelyArgumentCount];
89     var min = Math.max(0, likelyArgumentCount - 1 - 3);
90     for(var i = likelyArgumentCount - 1; i >= min; --i) {
91         ret.push(i);
92     }
93     for(var i = likelyArgumentCount + 1; i <= 3; ++i) {
94         ret.push(i);
95     }
96     return ret;
97 };
98
99 var argumentSequence = function(argumentCount) {
100     return util.filledRange(argumentCount, "_arg", "");
101 };
102
103 var parameterDeclaration = function(parameterCount) {
104     return util.filledRange(
105         Math.max(parameterCount, 3), "_arg", "");
106 };
107
108 var parameterCount = function(fn) {
109     if (typeof fn.length === "number") {
110         return Math.max(Math.min(fn.length, 1023 + 1), 0);
111     }
112     return 0;
113 };
114
115 makeNodePromisifiedEval =
116 function(callback, receiver, originalName, fn, _, multiArgs) {
117     var newParameterCount = Math.max(0, parameterCount(fn) - 1);
118     var argumentOrder = switchCaseArgumentOrder(newParameterCount);
119     var shouldProxyThis = typeof callback === "string" || receiver === THIS;
120
121     function generateCallForArgumentCount(count) {
122         var args = argumentSequence(count).join(", ");
123         var comma = count > 0 ? ", " : "";
124         var ret;
125         if (shouldProxyThis) {
126             ret = "ret = callback.call(this, {{args}}, nodeback); break;\n";
127         } else {
128             ret = receiver === undefined
129                 ? "ret = callback({{args}}, nodeback); break;\n"
130                 : "ret = callback.call(receiver, {{args}}, nodeback); break;\n";
131         }
132         return ret.replace("{{args}}", args).replace(", ", comma);
133     }
134
135     function generateArgumentSwitchCase() {
136         var ret = "";
137         for (var i = 0; i < argumentOrder.length; ++i) {
138             ret += "case " + argumentOrder[i] +":" +
139                 generateCallForArgumentCount(argumentOrder[i]);
140         }
141
142         ret += "                                                             \n\
143         default:                                                             \n\
144             var args = new Array(len + 1);                                   \n\
145             var i = 0;                                                       \n\
146             for (var i = 0; i < len; ++i) {                                  \n\
147                args[i] = arguments[i];                                       \n\
148             }                                                                \n\
149             args[i] = nodeback;                                              \n\
150             [CodeForCall]                                                    \n\
151             break;                                                           \n\
152         ".replace("[CodeForCall]", (shouldProxyThis
153                                 ? "ret = callback.apply(this, args);\n"
154                                 : "ret = callback.apply(receiver, args);\n"));
155         return ret;
156     }
157
158     var getFunctionCode = typeof callback === "string"
159                                 ? ("this != null ? this['"+callback+"'] : fn")
160                                 : "fn";
161     var body = "'use strict';                                                \n\
162         var ret = function (Parameters) {                                    \n\
163             'use strict';                                                    \n\
164             var len = arguments.length;                                      \n\
165             var promise = new Promise(INTERNAL);                             \n\
166             promise._captureStackTrace();                                    \n\
167             var nodeback = nodebackForPromise(promise, " + multiArgs + ");   \n\
168             var ret;                                                         \n\
169             var callback = tryCatch([GetFunctionCode]);                      \n\
170             switch(len) {                                                    \n\
171                 [CodeForSwitchCase]                                          \n\
172             }                                                                \n\
173             if (ret === errorObj) {                                          \n\
174                 promise._rejectCallback(maybeWrapAsError(ret.e), true, true);\n\
175             }                                                                \n\
176             if (!promise._isFateSealed()) promise._setAsyncGuaranteed();     \n\
177             return promise;                                                  \n\
178         };                                                                   \n\
179         notEnumerableProp(ret, '__isPromisified__', true);                   \n\
180         return ret;                                                          \n\
181     ".replace("[CodeForSwitchCase]", generateArgumentSwitchCase())
182         .replace("[GetFunctionCode]", getFunctionCode);
183     body = body.replace("Parameters", parameterDeclaration(newParameterCount));
184     return new Function("Promise",
185                         "fn",
186                         "receiver",
187                         "withAppended",
188                         "maybeWrapAsError",
189                         "nodebackForPromise",
190                         "tryCatch",
191                         "errorObj",
192                         "notEnumerableProp",
193                         "INTERNAL",
194                         body)(
195                     Promise,
196                     fn,
197                     receiver,
198                     withAppended,
199                     maybeWrapAsError,
200                     nodebackForPromise,
201                     util.tryCatch,
202                     util.errorObj,
203                     util.notEnumerableProp,
204                     INTERNAL);
205 };
206 }
207
208 function makeNodePromisifiedClosure(callback, receiver, _, fn, __, multiArgs) {
209     var defaultThis = (function() {return this;})();
210     var method = callback;
211     if (typeof method === "string") {
212         callback = fn;
213     }
214     function promisified() {
215         var _receiver = receiver;
216         if (receiver === THIS) _receiver = this;
217         var promise = new Promise(INTERNAL);
218         promise._captureStackTrace();
219         var cb = typeof method === "string" && this !== defaultThis
220             ? this[method] : callback;
221         var fn = nodebackForPromise(promise, multiArgs);
222         try {
223             cb.apply(_receiver, withAppended(arguments, fn));
224         } catch(e) {
225             promise._rejectCallback(maybeWrapAsError(e), true, true);
226         }
227         if (!promise._isFateSealed()) promise._setAsyncGuaranteed();
228         return promise;
229     }
230     util.notEnumerableProp(promisified, "__isPromisified__", true);
231     return promisified;
232 }
233
234 var makeNodePromisified = canEvaluate
235     ? makeNodePromisifiedEval
236     : makeNodePromisifiedClosure;
237
238 function promisifyAll(obj, suffix, filter, promisifier, multiArgs) {
239     var suffixRegexp = new RegExp(escapeIdentRegex(suffix) + "$");
240     var methods =
241         promisifiableMethods(obj, suffix, suffixRegexp, filter);
242
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, multiArgs);
250         } else {
251             var promisified = promisifier(fn, function() {
252                 return makeNodePromisified(key, THIS, key,
253                                            fn, suffix, multiArgs);
254             });
255             util.notEnumerableProp(promisified, "__isPromisified__", true);
256             obj[promisifiedKey] = promisified;
257         }
258     }
259     util.toFastProperties(obj);
260     return obj;
261 }
262
263 function promisify(callback, receiver, multiArgs) {
264     return makeNodePromisified(callback, receiver, undefined,
265                                 callback, null, multiArgs);
266 }
267
268 Promise.promisify = function (fn, options) {
269     if (typeof fn !== "function") {
270         throw new TypeError("expecting a function but got " + util.classString(fn));
271     }
272     if (isPromisified(fn)) {
273         return fn;
274     }
275     options = Object(options);
276     var receiver = options.context === undefined ? THIS : options.context;
277     var multiArgs = !!options.multiArgs;
278     var ret = promisify(fn, receiver, multiArgs);
279     util.copyDescriptors(fn, ret, propsFilter);
280     return ret;
281 };
282
283 Promise.promisifyAll = function (target, options) {
284     if (typeof target !== "function" && typeof target !== "object") {
285         throw new TypeError("the target of promisifyAll must be an object or a function\u000a\u000a    See http://goo.gl/MqrFmX\u000a");
286     }
287     options = Object(options);
288     var multiArgs = !!options.multiArgs;
289     var suffix = options.suffix;
290     if (typeof suffix !== "string") suffix = defaultSuffix;
291     var filter = options.filter;
292     if (typeof filter !== "function") filter = defaultFilter;
293     var promisifier = options.promisifier;
294     if (typeof promisifier !== "function") promisifier = makeNodePromisified;
295
296     if (!util.isIdentifier(suffix)) {
297         throw new RangeError("suffix must be a valid identifier\u000a\u000a    See http://goo.gl/MqrFmX\u000a");
298     }
299
300     var keys = util.inheritedDataKeys(target);
301     for (var i = 0; i < keys.length; ++i) {
302         var value = target[keys[i]];
303         if (keys[i] !== "constructor" &&
304             util.isClass(value)) {
305             promisifyAll(value.prototype, suffix, filter, promisifier,
306                 multiArgs);
307             promisifyAll(value, suffix, filter, promisifier, multiArgs);
308         }
309     }
310
311     return promisifyAll(target, suffix, filter, promisifier, multiArgs);
312 };
313 };
314