UNPKG

12.2 kBJavaScriptView Raw
1"use strict";
2module.exports = function(Promise, INTERNAL) {
3var THIS = {};
4var util = require("./util");
5var nodebackForPromise = require("./nodeback");
6var withAppended = util.withAppended;
7var maybeWrapAsError = util.maybeWrapAsError;
8var canEvaluate = util.canEvaluate;
9var TypeError = require("./errors").TypeError;
10var defaultSuffix = "Async";
11var defaultPromisified = {__isPromisified__: true};
12var noCopyProps = [
13 "arity", "length",
14 "name",
15 "arguments",
16 "caller",
17 "callee",
18 "prototype",
19 "__isPromisified__"
20];
21var noCopyPropsPattern = new RegExp("^(?:" + noCopyProps.join("|") + ")$");
22
23var defaultFilter = function(name) {
24 return util.isIdentifier(name) &&
25 name.charAt(0) !== "_" &&
26 name !== "constructor";
27};
28
29function propsFilter(key) {
30 return !noCopyPropsPattern.test(key);
31}
32
33function isPromisified(fn) {
34 try {
35 return fn.__isPromisified__ === true;
36 }
37 catch (e) {
38 return false;
39 }
40}
41
42function hasPromisified(obj, key, suffix) {
43 var val = util.getDataPropertyOrDefault(obj, key + suffix,
44 defaultPromisified);
45 return val ? isPromisified(val) : false;
46}
47function 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
62function 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
81var escapeIdentRegex = function(str) {
82 return str.replace(/([$])/, "\\$");
83};
84
85var makeNodePromisifiedEval;
86if (!false) {
87var 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
99var argumentSequence = function(argumentCount) {
100 return util.filledRange(argumentCount, "_arg", "");
101};
102
103var parameterDeclaration = function(parameterCount) {
104 return util.filledRange(
105 Math.max(parameterCount, 3), "_arg", "");
106};
107
108var 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
115makeNodePromisifiedEval =
116function(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
208function 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
234var makeNodePromisified = canEvaluate
235 ? makeNodePromisifiedEval
236 : makeNodePromisifiedClosure;
237
238function 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
263function promisify(callback, receiver, multiArgs) {
264 return makeNodePromisified(callback, receiver, undefined,
265 callback, null, multiArgs);
266}
267
268Promise.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
283Promise.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