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 315