| 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 |
|
|---|