Skip to content

Commit

Permalink
Fixes #111
Browse files Browse the repository at this point in the history
  • Loading branch information
petkaantonov committed Feb 15, 2014
1 parent 736d3cf commit 2245c86
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 30 deletions.
3 changes: 3 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ CONSTANT(FROM_PREVIOUS_EVENT, "From previous event:");
CONSTANT(THROW, 1);
CONSTANT(RETURN, 2);

//promisify.js
CONSTANT(MAX_PARAM_COUNT, 1023);
CONSTANT(PARAM_COUNTS_TO_TRY, 5);

//deprecated
CONSTANT(OBJECT_PROMISIFY_DEPRECATED, "Promise.promisify for promisifying entire objects is deprecated. Use Promise.promisifyAll instead.");
Expand Down
99 changes: 69 additions & 30 deletions src/promisify.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,51 @@ var inheritedMethods = (function() {
}
})();

function makeNodePromisifiedEval(callback, receiver, originalName) {
function getCall(count) {
//Gives an optimal sequence of argument count to try given a formal parameter
//.length for a function
function switchCaseArgumentOrder(likelyArgumentCount) {
var ret = [likelyArgumentCount];
var min = Math.max(0, likelyArgumentCount - 1 - PARAM_COUNTS_TO_TRY);
for(var i = likelyArgumentCount - 1; i >= min; --i) {
if (i === likelyArgumentCount) continue;
ret.push(i);
}
for(var i = likelyArgumentCount + 1; i <= PARAM_COUNTS_TO_TRY; ++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, MAX_PARAM_COUNT + 1), 0);
}
//Unsupported .length for functions
return 0;
}

function makeNodePromisifiedEval(callback, receiver, originalName, fn) {
ASSERT(typeof fn === "function");
//-1 for the callback parameter
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] = "a" + (i+1);
args[i] = "arguments[" + i + "]";
}
var comma = count > 0 ? "," : "";

Expand All @@ -95,42 +135,40 @@ function makeNodePromisifiedEval(callback, receiver, originalName) {
"break;";
}

function getArgs() {
return "var args = new Array(len + 1);" +
"var i = 0;" +
"for (var i = 0; i < len; ++i) { " +
" args[i] = arguments[i];" +
"}" +
"args[i] = fn;";
}
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;" +

var callbackName = (typeof originalName === "string" ?
originalName + "Async" :
"promisified");
(typeof callback === "string"
? "this['" + 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 +
"(a1, a2, a3, a4, a5) {\"use strict\";" +
"(" + parameterDeclaration(newParameterCount) + ") {\"use strict\";" +
"var len = arguments.length;" +
"var promise = new Promise(INTERNAL);"+
"promise._setTrace(" + callbackName + ", void 0);" +
"var fn = nodebackForPromise(promise);"+
"try{" +
"try {" +
"switch(len) {" +
"case 1:" + getCall(1) +
"case 2:" + getCall(2) +
"case 3:" + getCall(3) +
"case 0:" + getCall(0) +
"case 4:" + getCall(4) +
"case 5:" + getCall(5) +
"default: " + getArgs() + (typeof callback === "string"
? "this['" + callback + "'].apply("
: "callback.apply("
) +
(receiver === THIS ? "this" : "receiver") +
", args); break;" +
generateArgumentSwitchCase() +
"}" +
"}" +
"catch(e){ " +
Expand Down Expand Up @@ -185,7 +223,8 @@ function _promisify(callback, receiver, isAll) {
var promisifiedKey = key + AFTER_PROMISIFIED_SUFFIX;
notEnumerableProp(callback, originalKey, fn);
callback[promisifiedKey] =
makeNodePromisified(originalKey, THIS, key);
makeNodePromisified(originalKey, THIS,
key, fn);
}
//Right now the above loop will easily turn the
//object into hash table in V8
Expand All @@ -194,7 +233,7 @@ function _promisify(callback, receiver, isAll) {
return callback;
}
else {
return makeNodePromisified(callback, receiver, void 0);
return makeNodePromisified(callback, receiver, void 0, callback);
}
}

Expand Down
19 changes: 19 additions & 0 deletions test/mocha/promisify.js
Original file line number Diff line number Diff line change
Expand Up @@ -655,3 +655,22 @@ describe("RejectionError wrapping", function() {
});
});
});

if (typeof function(c){}.length === "number") {
describe("arity", function() {
specify("should be original - 1", function(done) {
var fn = function(a, b, c, callback) {};

assert.equal(Promise.promisify(fn).length, 3);

var o = {
fn: function(a, b, c, callback) {

}
};
assert.equal(Promise.promisifyAll(o).fnAsync.length, 3);

done();
})
})
}

0 comments on commit 2245c86

Please sign in to comment.