86763d60bae2a8ede78c33d1076ce39859039f2d
[aai/esr-gui.git] /
1 "use strict";
2 module.exports = function(Promise, INTERNAL) {
3 var THIS = {};
4 var util = require("./util.js");
5 var nodebackForPromise = require("./promise_resolver.js")
6     ._nodebackForPromise;
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};
13 var noCopyProps = [
14     "arity",    "length",
15     "name",
16     "arguments",
17     "caller",
18     "callee",
19     "prototype",
20     "__isPromisified__"
21 ];
22 var noCopyPropsPattern = new RegExp("^(?:" + noCopyProps.join("|") + ")$");
23
24 var defaultFilter = function(name) {
25     return util.isIdentifier(name) &&
26         name.charAt(0) !== "_" &&
27         name !== "constructor";
28 };
29
30 function propsFilter(key) {
31     return !noCopyPropsPattern.test(key);
32 }
33
34 function isPromisified(fn) {
35     try {
36         return fn.__isPromisified__ === true;
37     }
38     catch (e) {
39         return false;
40     }
41 }
42
43 function hasPromisified(obj, key, suffix) {
44     var val = util.getDataPropertyOrDefault(obj, key + suffix,
45                                             defaultPromisified);
46     return val ? isPromisified(val) : false;
47 }
48 function checkValid(ret, suffix, suffixRegexp) {
49     for (var i = 0; i < ret.length; i += 2) {
50         var key = ret[i];
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));
57                 }
58             }
59         }
60     }
61 }
62
63 function promisifiableMethods(obj, suffix, suffixRegexp, filter) {
64     var keys = util.inheritedDataKeys(obj);
65     var ret = [];
66     for (var i = 0; i < keys.length; ++i) {
67         var key = keys[i];
68         var value = obj[key];
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)) {
75             ret.push(key, value);
76         }
77     }
78     checkValid(ret, suffix, suffixRegexp);
79     return ret;
80 }
81
82 var escapeIdentRegex = function(str) {
83     return str.replace(/([$])/, "\\$");
84 };
85
86 var makeNodePromisifiedEval;
87 if (!false) {
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) {
92         ret.push(i);
93     }
94     for(var i = likelyArgumentCount + 1; i <= 3; ++i) {
95         ret.push(i);
96     }
97     return ret;
98 };
99
100 var argumentSequence = function(argumentCount) {
101     return util.filledRange(argumentCount, "_arg", "");
102 };
103
104 var parameterDeclaration = function(parameterCount) {
105     return util.filledRange(
106         Math.max(parameterCount, 3), "_arg", "");
107 };
108
109 var parameterCount = function(fn) {
110     if (typeof fn.length === "number") {
111         return Math.max(Math.min(fn.length, 1023 + 1), 0);
112     }
113     return 0;
114 };
115
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;
121
122     function generateCallForArgumentCount(count) {
123         var args = argumentSequence(count).join(", ");
124         var comma = count > 0 ? ", " : "";
125         var ret;
126         if (shouldProxyThis) {
127             ret = "ret = callback.call(this, {{args}}, nodeback); break;\n";
128         } else {
129             ret = receiver === undefined
130                 ? "ret = callback({{args}}, nodeback); break;\n"
131                 : "ret = callback.call(receiver, {{args}}, nodeback); break;\n";
132         }
133         return ret.replace("{{args}}", args).replace(", ", comma);
134     }
135
136     function generateArgumentSwitchCase() {
137         var ret = "";
138         for (var i = 0; i < argumentOrder.length; ++i) {
139             ret += "case " + argumentOrder[i] +":" +
140                 generateCallForArgumentCount(argumentOrder[i]);
141         }
142
143         ret += "                                                             \n\
144         default:                                                             \n\
145             var args = new Array(len + 1);                                   \n\
146             var i = 0;                                                       \n\
147             for (var i = 0; i < len; ++i) {                                  \n\
148                args[i] = arguments[i];                                       \n\
149             }                                                                \n\
150             args[i] = nodeback;                                              \n\
151             [CodeForCall]                                                    \n\
152             break;                                                           \n\
153         ".replace("[CodeForCall]", (shouldProxyThis
154                                 ? "ret = callback.apply(this, args);\n"
155                                 : "ret = callback.apply(receiver, args);\n"));
156         return ret;
157     }
158
159     var getFunctionCode = typeof callback === "string"
160                                 ? ("this != null ? this['"+callback+"'] : fn")
161                                 : "fn";
162
163     return new Function("Promise",
164                         "fn",
165                         "receiver",
166                         "withAppended",
167                         "maybeWrapAsError",
168                         "nodebackForPromise",
169                         "tryCatch",
170                         "errorObj",
171                         "notEnumerableProp",
172                         "INTERNAL","'use strict';                            \n\
173         var ret = function (Parameters) {                                    \n\
174             'use strict';                                                    \n\
175             var len = arguments.length;                                      \n\
176             var promise = new Promise(INTERNAL);                             \n\
177             promise._captureStackTrace();                                    \n\
178             var nodeback = nodebackForPromise(promise);                      \n\
179             var ret;                                                         \n\
180             var callback = tryCatch([GetFunctionCode]);                      \n\
181             switch(len) {                                                    \n\
182                 [CodeForSwitchCase]                                          \n\
183             }                                                                \n\
184             if (ret === errorObj) {                                          \n\
185                 promise._rejectCallback(maybeWrapAsError(ret.e), true, true);\n\
186             }                                                                \n\
187             return promise;                                                  \n\
188         };                                                                   \n\
189         notEnumerableProp(ret, '__isPromisified__', true);                   \n\
190         return ret;                                                          \n\
191         "
192         .replace("Parameters", parameterDeclaration(newParameterCount))
193         .replace("[CodeForSwitchCase]", generateArgumentSwitchCase())
194         .replace("[GetFunctionCode]", getFunctionCode))(
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
209 function makeNodePromisifiedClosure(callback, receiver, _, fn) {
210     var defaultThis = (function() {return this;})();
211     var method = callback;
212     if (typeof method === "string") {
213         callback = fn;
214     }
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);
223         try {
224             cb.apply(_receiver, withAppended(arguments, fn));
225         } catch(e) {
226             promise._rejectCallback(maybeWrapAsError(e), true, true);
227         }
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) {
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);
250         } else {
251             var promisified = promisifier(fn, function() {
252                 return makeNodePromisified(key, THIS, key, fn, suffix);
253             });
254             util.notEnumerableProp(promisified, "__isPromisified__", true);
255             obj[promisifiedKey] = promisified;
256         }
257     }
258     util.toFastProperties(obj);
259     return obj;
260 }
261
262 function promisify(callback, receiver) {
263     return makeNodePromisified(callback, receiver, undefined, callback);
264 }
265
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");
269     }
270     if (isPromisified(fn)) {
271         return fn;
272     }
273     var ret = promisify(fn, arguments.length < 2 ? THIS : receiver);
274     util.copyDescriptors(fn, ret, propsFilter);
275     return ret;
276 };
277
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");
281     }
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;
289
290     if (!util.isIdentifier(suffix)) {
291         throw new RangeError("suffix must be a valid identifier\u000a\u000a    See http://goo.gl/8FZo5V\u000a");
292     }
293
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);
301         }
302     }
303
304     return promisifyAll(target, suffix, filter, promisifier);
305 };
306 };
307