/** * Copyright (c) 2014 Petka Antonov * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions:

* * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ "use strict"; module.exports = function(Promise, INTERNAL) { var THIS = {}; var util = require("./util.js"); var es5 = require("./es5.js"); var nodebackForPromise = require("./promise_resolver.js") ._nodebackForPromise; var withAppended = util.withAppended; var maybeWrapAsError = util.maybeWrapAsError; var canEvaluate = util.canEvaluate; var notEnumerableProp = util.notEnumerableProp; var deprecated = util.deprecated; var ASSERT = require("./assert.js"); var roriginal = new RegExp("__beforePromisified__" + "$"); var hasProp = {}.hasOwnProperty; function isPromisified(fn) { return fn.__isPromisified__ === true; } var inheritedMethods = (function() { if (es5.isES5) { var create = Object.create; var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; return function(cur) { var original = cur; var ret = []; var visitedKeys = create(null); while (cur !== null) { var keys = es5.keys(cur); for (var i = 0, len = keys.length; i < len; ++i) { var key = keys[i]; if (visitedKeys[key] || roriginal.test(key) || hasProp.call(original, key + "__beforePromisified__") ) { continue; } visitedKeys[key] = true; var desc = getOwnPropertyDescriptor(cur, key); if (desc != null && typeof desc.value === "function" && !isPromisified(desc.value)) { ret.push(key, desc.value); } } cur = es5.getPrototypeOf(cur); } return ret; }; } else { return function(obj) { var ret = []; /*jshint forin:false */ for (var key in obj) { if (roriginal.test(key) || hasProp.call(obj, key + "__beforePromisified__")) { continue; } var fn = obj[key]; if (typeof fn === "function" && !isPromisified(fn)) { ret.push(key, fn); } } return ret; }; } })(); function switchCaseArgumentOrder(likelyArgumentCount) { var ret = [likelyArgumentCount]; var min = Math.max(0, likelyArgumentCount - 1 - 5); for(var i = likelyArgumentCount - 1; i >= min; --i) { if (i === likelyArgumentCount) continue; ret.push(i); } for(var i = likelyArgumentCount + 1; i <= 5; ++i) { ret.push(i); } return ret; } function parameterDeclaration(parameterCount) { var ret = new Array(parameterCount); for(var i = 0; i < ret.length; ++i) { ret[i] = "_arg" + i; } return ret.join(", "); } function parameterCount(fn) { if (typeof fn.length === "number") { return Math.max(Math.min(fn.length, 1023 + 1), 0); } return 0; } function propertyAccess(id) { var rident = /^[a-z$_][a-z$_0-9]*$/i; if (rident.test(id)) { return "." + id; } else return "['" + id.replace(/(['\\])/g, "\\$1") + "']"; } function makeNodePromisifiedEval(callback, receiver, originalName, fn) { var newParameterCount = Math.max(0, parameterCount(fn) - 1); var argumentOrder = switchCaseArgumentOrder(newParameterCount); var callbackName = (typeof originalName === "string" ? originalName + "Async" : "promisified"); function generateCallForArgumentCount(count) { var args = new Array(count); for (var i = 0, len = args.length; i < len; ++i) { args[i] = "arguments[" + i + "]"; } var comma = count > 0 ? "," : ""; if (typeof callback === "string" && receiver === THIS) { return "this" + propertyAccess(callback) + "("+args.join(",") + comma +" fn);"+ "break;"; } return (receiver === void 0 ? "callback("+args.join(",")+ comma +" fn);" : "callback.call("+(receiver === THIS ? "this" : "receiver")+", "+args.join(",") + comma + " fn);") + "break;"; } function generateArgumentSwitchCase() { var ret = ""; for(var i = 0; i < argumentOrder.length; ++i) { ret += "case " + argumentOrder[i] +":" + generateCallForArgumentCount(argumentOrder[i]); } ret += "default: var args = new Array(len + 1);" + "var i = 0;" + "for (var i = 0; i < len; ++i) { " + " args[i] = arguments[i];" + "}" + "args[i] = fn;" + (typeof callback === "string" ? "this" + propertyAccess(callback) + ".apply(" : "callback.apply(") + (receiver === THIS ? "this" : "receiver") + ", args); break;"; return ret; } return new Function("Promise", "callback", "receiver", "withAppended", "maybeWrapAsError", "nodebackForPromise", "INTERNAL", "var ret = function " + callbackName + "(" + parameterDeclaration(newParameterCount) + ") {\"use strict\";" + "var len = arguments.length;" + "var promise = new Promise(INTERNAL);"+ "promise._setTrace(" + callbackName + ", void 0);" + "var fn = nodebackForPromise(promise);"+ "try {" + "switch(len) {" + generateArgumentSwitchCase() + "}" + "}" + "catch(e){ " + "var wrapped = maybeWrapAsError(e);" + "promise._attachExtraTrace(wrapped);" + "promise._reject(wrapped);" + "}" + "return promise;" + "" + "}; ret.__isPromisified__ = true; return ret;" )(Promise, callback, receiver, withAppended, maybeWrapAsError, nodebackForPromise, INTERNAL); } function makeNodePromisifiedClosure(callback, receiver) { function promisified() { var _receiver = receiver; if (receiver === THIS) _receiver = this; if (typeof callback === "string") { callback = _receiver[callback]; } var promise = new Promise(INTERNAL); promise._setTrace(promisified, void 0); var fn = nodebackForPromise(promise); try { callback.apply(_receiver, withAppended(arguments, fn)); } catch(e) { var wrapped = maybeWrapAsError(e); promise._attachExtraTrace(wrapped); promise._reject(wrapped); } return promise; } promisified.__isPromisified__ = true; return promisified; } var makeNodePromisified = canEvaluate ? makeNodePromisifiedEval : makeNodePromisifiedClosure; function f(){} function _promisify(callback, receiver, isAll) { if (isAll) { var methods = inheritedMethods(callback); for (var i = 0, len = methods.length; i < len; i+= 2) { var key = methods[i]; var fn = methods[i+1]; var originalKey = key + "__beforePromisified__"; var promisifiedKey = key + "Async"; notEnumerableProp(callback, originalKey, fn); callback[promisifiedKey] = makeNodePromisified(originalKey, THIS, key, fn); } if (methods.length > 16) f.prototype = callback; return callback; } else { return makeNodePromisified(callback, receiver, void 0, callback); } } Promise.promisify = function Promise$Promisify(fn, receiver) { if (typeof fn === "object" && fn !== null) { deprecated("Promise.promisify for promisifying entire objects is deprecated. Use Promise.promisifyAll instead."); return _promisify(fn, receiver, true); } if (typeof fn !== "function") { throw new TypeError("fn must be a function"); } if (isPromisified(fn)) { return fn; } return _promisify( fn, arguments.length < 2 ? THIS : receiver, false); }; Promise.promisifyAll = function Promise$PromisifyAll(target) { if (typeof target !== "function" && typeof target !== "object") { throw new TypeError("the target of promisifyAll must be an object or a function"); } return _promisify(target, void 0, true); }; };